@@ -43,6 +43,24 @@ struct tx_packet_hdr {
struct rfc_1042_hdr rfc1042_hdr;
} __packed;
+struct mwifiex_fw_header {
+ __le32 dnld_cmd;
+ __le32 base_addr;
+ __le32 data_length;
+ __le32 crc;
+} __packed;
+
+struct mwifiex_fw_data {
+ struct mwifiex_fw_header header;
+ __le32 seq_num;
+ u8 data[1];
+} __packed;
+
+#define MWIFIEX_FW_DNLD_CMD_1 0x1
+#define MWIFIEX_FW_DNLD_CMD_5 0x5
+#define MWIFIEX_FW_DNLD_CMD_6 0x6
+#define MWIFIEX_FW_DNLD_CMD_7 0x7
+
#define B_SUPPORTED_RATES 5
#define G_SUPPORTED_RATES 9
#define BG_SUPPORTED_RATES 13
@@ -1956,6 +1956,63 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter,
return ret;
}
+/* Extract wifi part from wifi-bt combo firmware image.
+ */
+
+static int mwifiex_extract_wifi_fw(struct mwifiex_adapter *adapter,
+ u8 *firmware, u32 firmware_len) {
+ struct mwifiex_fw_data fwdata;
+ u32 offset = 0, data_len, dnld_cmd;
+ int ret = 0;
+ bool cmd7_before = false;
+
+ while (1) {
+ if (offset + sizeof(fwdata.header) >= firmware_len) {
+ mwifiex_dbg(adapter, ERROR,
+ "extract wifi-only firmware failure!");
+ ret = -1;
+ goto done;
+ }
+
+ memcpy(&fwdata.header, firmware + offset,
+ sizeof(fwdata.header));
+ dnld_cmd = le32_to_cpu(fwdata.header.dnld_cmd);
+ data_len = le32_to_cpu(fwdata.header.data_length);
+
+ switch (dnld_cmd) {
+ case MWIFIEX_FW_DNLD_CMD_1:
+ if (!cmd7_before) {
+ mwifiex_dbg(adapter, ERROR,
+ "no cmd7 before cmd1!");
+ ret = -1;
+ goto done;
+ }
+ offset += data_len + sizeof(fwdata.header);
+ break;
+ case MWIFIEX_FW_DNLD_CMD_5:
+ offset += data_len + sizeof(fwdata.header);
+ break;
+ case MWIFIEX_FW_DNLD_CMD_6:
+ offset += data_len + sizeof(fwdata.header);
+ ret = offset;
+ goto done;
+ case MWIFIEX_FW_DNLD_CMD_7:
+ if (!cmd7_before)
+ cmd7_before = true;
+ offset += sizeof(fwdata.header);
+ break;
+ default:
+ mwifiex_dbg(adapter, ERROR, "unknown dnld_cmd %d\n",
+ dnld_cmd);
+ ret = -1;
+ goto done;
+ }
+ }
+
+done:
+ return ret;
+}
+
/*
* This function downloads the firmware to the card.
*
@@ -1971,7 +2028,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
u32 firmware_len = fw->fw_len;
u32 offset = 0;
struct sk_buff *skb;
- u32 txlen, tx_blocks = 0, tries, len;
+ u32 txlen, tx_blocks = 0, tries, len, val;
u32 block_retry_cnt = 0;
struct pcie_service_card *card = adapter->card;
const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
@@ -1998,6 +2055,24 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
goto done;
}
+ ret = mwifiex_read_reg(adapter, PCIE_SCRATCH_13_REG, &val);
+ if (ret) {
+ mwifiex_dbg(adapter, FATAL, "Failed to read scratch register 13\n");
+ goto done;
+ }
+
+ /* PCIE FLR case: extract wifi part from combo firmware*/
+ if (val == MWIFIEX_PCIE_FLR_HAPPENS) {
+ ret = mwifiex_extract_wifi_fw(adapter, firmware, firmware_len);
+ if (ret < 0) {
+ mwifiex_dbg(adapter, ERROR, "Failed to extract wifi fw\n");
+ goto done;
+ }
+ offset = ret;
+ mwifiex_dbg(adapter, MSG,
+ "info: dnld wifi firmware from %d bytes\n", offset);
+ }
+
/* Perform firmware data transfer */
do {
u32 ireg_intr = 0;
@@ -3060,12 +3135,6 @@ static void mwifiex_pcie_up_dev(struct mwifiex_adapter *adapter)
struct pci_dev *pdev = card->dev;
const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
- /* Bluetooth is not on pcie interface. Download Wifi only firmware
- * during pcie FLR, so that bluetooth part of firmware which is
- * already running doesn't get affected.
- */
- strcpy(adapter->fw_name, PCIE8997_DEFAULT_WIFIFW_NAME);
-
/* tx_buf_size might be changed to 3584 by firmware during
* data transfer, we should reset it to default size.
*/
@@ -120,6 +120,8 @@
#define MWIFIEX_SLEEP_COOKIE_SIZE 4
#define MWIFIEX_MAX_DELAY_COUNT 100
+#define MWIFIEX_PCIE_FLR_HAPPENS 0xFEDCBABA
+
struct mwifiex_pcie_card_reg {
u16 cmd_addr_lo;
u16 cmd_addr_hi;