hal.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  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 void 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. cmd->seq_num = 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. }
  122. u16 iwm_alloc_wifi_cmd_seq(struct iwm_priv *iwm)
  123. {
  124. u16 seq_num = iwm->wifi_seq_num;
  125. iwm->wifi_seq_num++;
  126. iwm->wifi_seq_num %= UMAC_WIFI_SEQ_NUM_MAX;
  127. return seq_num;
  128. }
  129. static void iwm_wifi_cmd_init(struct iwm_priv *iwm,
  130. struct iwm_wifi_cmd *cmd,
  131. struct iwm_udma_wifi_cmd *udma_cmd,
  132. struct iwm_umac_cmd *umac_cmd,
  133. struct iwm_lmac_cmd *lmac_cmd,
  134. u16 payload_size)
  135. {
  136. INIT_LIST_HEAD(&cmd->pending);
  137. spin_lock(&iwm->cmd_lock);
  138. cmd->seq_num = iwm_alloc_wifi_cmd_seq(iwm);
  139. umac_cmd->seq_num = cpu_to_le16(cmd->seq_num);
  140. if (umac_cmd->resp)
  141. list_add_tail(&cmd->pending, &iwm->wifi_pending_cmd);
  142. spin_unlock(&iwm->cmd_lock);
  143. cmd->buf.start = cmd->buf.payload;
  144. cmd->buf.len = 0;
  145. if (lmac_cmd) {
  146. cmd->buf.start -= sizeof(struct iwm_lmac_hdr);
  147. lmac_cmd->seq_num = cpu_to_le16(cmd->seq_num);
  148. lmac_cmd->count = cpu_to_le16(payload_size);
  149. memcpy(&cmd->lmac_cmd, lmac_cmd, sizeof(*lmac_cmd));
  150. umac_cmd->count = cpu_to_le16(sizeof(struct iwm_lmac_hdr));
  151. } else
  152. umac_cmd->count = 0;
  153. umac_cmd->count = cpu_to_le16(payload_size +
  154. le16_to_cpu(umac_cmd->count));
  155. udma_cmd->count = cpu_to_le16(sizeof(struct iwm_umac_fw_cmd_hdr) +
  156. le16_to_cpu(umac_cmd->count));
  157. memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd));
  158. memcpy(&cmd->umac_cmd, umac_cmd, sizeof(*umac_cmd));
  159. }
  160. void iwm_cmd_flush(struct iwm_priv *iwm)
  161. {
  162. struct iwm_wifi_cmd *wcmd, *wnext;
  163. struct iwm_nonwifi_cmd *nwcmd, *nwnext;
  164. list_for_each_entry_safe(wcmd, wnext, &iwm->wifi_pending_cmd, pending) {
  165. list_del(&wcmd->pending);
  166. kfree(wcmd);
  167. }
  168. list_for_each_entry_safe(nwcmd, nwnext, &iwm->nonwifi_pending_cmd,
  169. pending) {
  170. list_del(&nwcmd->pending);
  171. kfree(nwcmd);
  172. }
  173. }
  174. struct iwm_wifi_cmd *iwm_get_pending_wifi_cmd(struct iwm_priv *iwm, u16 seq_num)
  175. {
  176. struct iwm_wifi_cmd *cmd, *next;
  177. list_for_each_entry_safe(cmd, next, &iwm->wifi_pending_cmd, pending)
  178. if (cmd->seq_num == seq_num) {
  179. list_del(&cmd->pending);
  180. return cmd;
  181. }
  182. return NULL;
  183. }
  184. struct iwm_nonwifi_cmd *
  185. iwm_get_pending_nonwifi_cmd(struct iwm_priv *iwm, u8 seq_num, u8 cmd_opcode)
  186. {
  187. struct iwm_nonwifi_cmd *cmd, *next;
  188. list_for_each_entry_safe(cmd, next, &iwm->nonwifi_pending_cmd, pending)
  189. if ((cmd->seq_num == seq_num) &&
  190. (cmd->udma_cmd.opcode == cmd_opcode) &&
  191. (cmd->resp_received)) {
  192. list_del(&cmd->pending);
  193. return cmd;
  194. }
  195. return NULL;
  196. }
  197. static void iwm_build_udma_nonwifi_hdr(struct iwm_priv *iwm,
  198. struct iwm_udma_out_nonwifi_hdr *hdr,
  199. struct iwm_udma_nonwifi_cmd *cmd)
  200. {
  201. memset(hdr, 0, sizeof(*hdr));
  202. SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, cmd->opcode);
  203. SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_RESP, cmd->resp);
  204. SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, 1);
  205. SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW,
  206. cmd->handle_by_hw);
  207. SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE);
  208. SET_VAL32(hdr->cmd, UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM,
  209. le16_to_cpu(cmd->seq_num));
  210. hdr->addr = cmd->addr;
  211. hdr->op1_sz = cmd->op1_sz;
  212. hdr->op2 = cmd->op2;
  213. }
  214. static int iwm_send_udma_nonwifi_cmd(struct iwm_priv *iwm,
  215. struct iwm_nonwifi_cmd *cmd)
  216. {
  217. struct iwm_udma_out_nonwifi_hdr *udma_hdr;
  218. struct iwm_nonwifi_cmd_buff *buf;
  219. struct iwm_udma_nonwifi_cmd *udma_cmd = &cmd->udma_cmd;
  220. buf = &cmd->buf;
  221. buf->start -= sizeof(struct iwm_umac_nonwifi_out_hdr);
  222. buf->len += sizeof(struct iwm_umac_nonwifi_out_hdr);
  223. udma_hdr = (struct iwm_udma_out_nonwifi_hdr *)(buf->start);
  224. iwm_build_udma_nonwifi_hdr(iwm, udma_hdr, udma_cmd);
  225. IWM_DBG_CMD(iwm, DBG,
  226. "Send UDMA nonwifi cmd: opcode = 0x%x, resp = 0x%x, "
  227. "hw = 0x%x, seqnum = %d, addr = 0x%x, op1_sz = 0x%x, "
  228. "op2 = 0x%x\n", udma_cmd->opcode, udma_cmd->resp,
  229. udma_cmd->handle_by_hw, cmd->seq_num, udma_cmd->addr,
  230. udma_cmd->op1_sz, udma_cmd->op2);
  231. return iwm_bus_send_chunk(iwm, buf->start, buf->len);
  232. }
  233. void iwm_udma_wifi_hdr_set_eop(struct iwm_priv *iwm, u8 *buf, u8 eop)
  234. {
  235. struct iwm_udma_out_wifi_hdr *hdr = (struct iwm_udma_out_wifi_hdr *)buf;
  236. SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, eop);
  237. }
  238. void iwm_build_udma_wifi_hdr(struct iwm_priv *iwm,
  239. struct iwm_udma_out_wifi_hdr *hdr,
  240. struct iwm_udma_wifi_cmd *cmd)
  241. {
  242. memset(hdr, 0, sizeof(*hdr));
  243. SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, UMAC_HDI_OUT_OPCODE_WIFI);
  244. SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, cmd->eop);
  245. SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE);
  246. SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_BYTE_COUNT,
  247. le16_to_cpu(cmd->count));
  248. SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_CREDIT_GRP, cmd->credit_group);
  249. SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_RATID, cmd->ra_tid);
  250. SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_LMAC_OFFSET, cmd->lmac_offset);
  251. }
  252. void iwm_build_umac_hdr(struct iwm_priv *iwm,
  253. struct iwm_umac_fw_cmd_hdr *hdr,
  254. struct iwm_umac_cmd *cmd)
  255. {
  256. memset(hdr, 0, sizeof(*hdr));
  257. SET_VAL32(hdr->meta_data, UMAC_FW_CMD_BYTE_COUNT,
  258. le16_to_cpu(cmd->count));
  259. SET_VAL32(hdr->meta_data, UMAC_FW_CMD_TX_STA_COLOR, cmd->color);
  260. SET_VAL8(hdr->cmd.flags, UMAC_DEV_CMD_FLAGS_RESP_REQ, cmd->resp);
  261. hdr->cmd.cmd = cmd->id;
  262. hdr->cmd.seq_num = cmd->seq_num;
  263. }
  264. static int iwm_send_udma_wifi_cmd(struct iwm_priv *iwm,
  265. struct iwm_wifi_cmd *cmd)
  266. {
  267. struct iwm_umac_wifi_out_hdr *umac_hdr;
  268. struct iwm_wifi_cmd_buff *buf;
  269. struct iwm_udma_wifi_cmd *udma_cmd = &cmd->udma_cmd;
  270. struct iwm_umac_cmd *umac_cmd = &cmd->umac_cmd;
  271. int ret;
  272. buf = &cmd->buf;
  273. buf->start -= sizeof(struct iwm_umac_wifi_out_hdr);
  274. buf->len += sizeof(struct iwm_umac_wifi_out_hdr);
  275. umac_hdr = (struct iwm_umac_wifi_out_hdr *)(buf->start);
  276. iwm_build_udma_wifi_hdr(iwm, &umac_hdr->hw_hdr, udma_cmd);
  277. iwm_build_umac_hdr(iwm, &umac_hdr->sw_hdr, umac_cmd);
  278. IWM_DBG_CMD(iwm, DBG,
  279. "Send UDMA wifi cmd: opcode = 0x%x, UMAC opcode = 0x%x, "
  280. "eop = 0x%x, count = 0x%x, credit_group = 0x%x, "
  281. "ra_tid = 0x%x, lmac_offset = 0x%x, seqnum = %d\n",
  282. UMAC_HDI_OUT_OPCODE_WIFI, umac_cmd->id,
  283. udma_cmd->eop, udma_cmd->count, udma_cmd->credit_group,
  284. udma_cmd->ra_tid, udma_cmd->lmac_offset, cmd->seq_num);
  285. if (umac_cmd->id == UMAC_CMD_OPCODE_WIFI_PASS_THROUGH)
  286. IWM_DBG_CMD(iwm, DBG, "\tLMAC opcode: 0x%x\n",
  287. cmd->lmac_cmd.id);
  288. ret = iwm_tx_credit_alloc(iwm, udma_cmd->credit_group, buf->len);
  289. /* We keep sending UMAC reset regardless of the command credits.
  290. * The UMAC is supposed to be reset anyway and the Tx credits are
  291. * reinitialized afterwards. If we are lucky, the reset could
  292. * still be done even though we have run out of credits for the
  293. * command pool at this moment.*/
  294. if (ret && (umac_cmd->id != UMAC_CMD_OPCODE_RESET)) {
  295. IWM_DBG_TX(iwm, DBG, "Failed to alloc tx credit for cmd %d\n",
  296. umac_cmd->id);
  297. return ret;
  298. }
  299. return iwm_bus_send_chunk(iwm, buf->start, buf->len);
  300. }
  301. /* target_cmd a.k.a udma_nonwifi_cmd can be sent when UMAC is not available */
  302. int iwm_hal_send_target_cmd(struct iwm_priv *iwm,
  303. struct iwm_udma_nonwifi_cmd *udma_cmd,
  304. const void *payload)
  305. {
  306. struct iwm_nonwifi_cmd *cmd;
  307. int ret;
  308. cmd = kzalloc(sizeof(struct iwm_nonwifi_cmd), GFP_KERNEL);
  309. if (!cmd) {
  310. IWM_ERR(iwm, "Couldn't alloc memory for hal cmd\n");
  311. return -ENOMEM;
  312. }
  313. iwm_nonwifi_cmd_init(iwm, cmd, udma_cmd);
  314. if (cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE ||
  315. cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE_PERSISTENT) {
  316. cmd->buf.len = le32_to_cpu(cmd->udma_cmd.op1_sz);
  317. memcpy(&cmd->buf.payload, payload, cmd->buf.len);
  318. }
  319. ret = iwm_send_udma_nonwifi_cmd(iwm, cmd);
  320. if (!udma_cmd->resp)
  321. kfree(cmd);
  322. if (ret < 0)
  323. return ret;
  324. return cmd->seq_num;
  325. }
  326. static void iwm_build_lmac_hdr(struct iwm_priv *iwm, struct iwm_lmac_hdr *hdr,
  327. struct iwm_lmac_cmd *cmd)
  328. {
  329. memset(hdr, 0, sizeof(*hdr));
  330. hdr->id = cmd->id;
  331. hdr->flags = 0; /* Is this ever used? */
  332. hdr->seq_num = cmd->seq_num;
  333. }
  334. /*
  335. * iwm_hal_send_host_cmd(): sends commands to the UMAC or the LMAC.
  336. * Sending command to the LMAC is equivalent to sending a
  337. * regular UMAC command with the LMAC passtrough or the LMAC
  338. * wrapper UMAC command IDs.
  339. */
  340. int iwm_hal_send_host_cmd(struct iwm_priv *iwm,
  341. struct iwm_udma_wifi_cmd *udma_cmd,
  342. struct iwm_umac_cmd *umac_cmd,
  343. struct iwm_lmac_cmd *lmac_cmd,
  344. const void *payload, u16 payload_size)
  345. {
  346. struct iwm_wifi_cmd *cmd;
  347. struct iwm_lmac_hdr *hdr;
  348. int lmac_hdr_len = 0;
  349. int ret;
  350. cmd = kzalloc(sizeof(struct iwm_wifi_cmd), GFP_KERNEL);
  351. if (!cmd) {
  352. IWM_ERR(iwm, "Couldn't alloc memory for wifi hal cmd\n");
  353. return -ENOMEM;
  354. }
  355. iwm_wifi_cmd_init(iwm, cmd, udma_cmd, umac_cmd, lmac_cmd, payload_size);
  356. if (lmac_cmd) {
  357. hdr = (struct iwm_lmac_hdr *)(cmd->buf.start);
  358. iwm_build_lmac_hdr(iwm, hdr, &cmd->lmac_cmd);
  359. lmac_hdr_len = sizeof(struct iwm_lmac_hdr);
  360. }
  361. memcpy(cmd->buf.payload, payload, payload_size);
  362. cmd->buf.len = le16_to_cpu(umac_cmd->count);
  363. ret = iwm_send_udma_wifi_cmd(iwm, cmd);
  364. /* We free the cmd if we're not expecting any response */
  365. if (!umac_cmd->resp)
  366. kfree(cmd);
  367. return ret;
  368. }
  369. /*
  370. * iwm_hal_send_umac_cmd(): This is a special case for
  371. * iwm_hal_send_host_cmd() to send direct UMAC cmd (without
  372. * LMAC involved).
  373. */
  374. int iwm_hal_send_umac_cmd(struct iwm_priv *iwm,
  375. struct iwm_udma_wifi_cmd *udma_cmd,
  376. struct iwm_umac_cmd *umac_cmd,
  377. const void *payload, u16 payload_size)
  378. {
  379. return iwm_hal_send_host_cmd(iwm, udma_cmd, umac_cmd, NULL,
  380. payload, payload_size);
  381. }