|
@@ -37,6 +37,13 @@ static struct mwifiex_if_ops pcie_ops;
|
|
|
|
|
|
static struct semaphore add_remove_card_sem;
|
|
|
|
|
|
+static struct memory_type_mapping mem_type_mapping_tbl[] = {
|
|
|
+ {"ITCM", NULL, 0, 0xF0},
|
|
|
+ {"DTCM", NULL, 0, 0xF1},
|
|
|
+ {"SQRAM", NULL, 0, 0xF2},
|
|
|
+ {"IRAM", NULL, 0, 0xF3},
|
|
|
+};
|
|
|
+
|
|
|
static int
|
|
|
mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb,
|
|
|
size_t size, int flags)
|
|
@@ -192,6 +199,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
|
|
|
card->pcie.reg = data->reg;
|
|
|
card->pcie.blksz_fw_dl = data->blksz_fw_dl;
|
|
|
card->pcie.tx_buf_size = data->tx_buf_size;
|
|
|
+ card->pcie.supports_fw_dump = data->supports_fw_dump;
|
|
|
}
|
|
|
|
|
|
if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops,
|
|
@@ -221,6 +229,8 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev)
|
|
|
if (!adapter || !adapter->priv_num)
|
|
|
return;
|
|
|
|
|
|
+ cancel_work_sync(&adapter->iface_work);
|
|
|
+
|
|
|
if (user_rmmod) {
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
|
if (adapter->is_suspended)
|
|
@@ -307,6 +317,17 @@ static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* This function reads u8 data from PCIE card register. */
|
|
|
+static int mwifiex_read_reg_byte(struct mwifiex_adapter *adapter,
|
|
|
+ int reg, u8 *data)
|
|
|
+{
|
|
|
+ struct pcie_service_card *card = adapter->card;
|
|
|
+
|
|
|
+ *data = ioread8(card->pci_mmap1 + reg);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* This function adds delay loop to ensure FW is awake before proceeding.
|
|
|
*/
|
|
@@ -2173,6 +2194,174 @@ static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* This function read/write firmware */
|
|
|
+static enum rdwr_status
|
|
|
+mwifiex_pcie_rdwr_firmware(struct mwifiex_adapter *adapter, u8 doneflag)
|
|
|
+{
|
|
|
+ int ret, tries;
|
|
|
+ u8 ctrl_data;
|
|
|
+ struct pcie_service_card *card = adapter->card;
|
|
|
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
|
|
|
+
|
|
|
+ ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, FW_DUMP_HOST_READY);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(adapter->dev, "PCIE write err\n");
|
|
|
+ return RDWR_STATUS_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
|
|
|
+ mwifiex_read_reg_byte(adapter, reg->fw_dump_ctrl, &ctrl_data);
|
|
|
+ if (ctrl_data == FW_DUMP_DONE)
|
|
|
+ return RDWR_STATUS_SUCCESS;
|
|
|
+ if (doneflag && ctrl_data == doneflag)
|
|
|
+ return RDWR_STATUS_DONE;
|
|
|
+ if (ctrl_data != FW_DUMP_HOST_READY) {
|
|
|
+ dev_info(adapter->dev,
|
|
|
+ "The ctrl reg was changed, re-try again!\n");
|
|
|
+ mwifiex_write_reg(adapter, reg->fw_dump_ctrl,
|
|
|
+ FW_DUMP_HOST_READY);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(adapter->dev, "PCIE write err\n");
|
|
|
+ return RDWR_STATUS_FAILURE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ usleep_range(100, 200);
|
|
|
+ }
|
|
|
+
|
|
|
+ dev_err(adapter->dev, "Fail to pull ctrl_data\n");
|
|
|
+ return RDWR_STATUS_FAILURE;
|
|
|
+}
|
|
|
+
|
|
|
+/* This function dump firmware memory to file */
|
|
|
+static void mwifiex_pcie_fw_dump_work(struct mwifiex_adapter *adapter)
|
|
|
+{
|
|
|
+ struct pcie_service_card *card = adapter->card;
|
|
|
+ const struct mwifiex_pcie_card_reg *creg = card->pcie.reg;
|
|
|
+ unsigned int reg, reg_start, reg_end;
|
|
|
+ struct timeval t;
|
|
|
+ u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0;
|
|
|
+ enum rdwr_status stat;
|
|
|
+ u32 memory_size;
|
|
|
+ static char *env[] = { "DRIVER=mwifiex_pcie", "EVENT=fw_dump", NULL };
|
|
|
+
|
|
|
+ if (!card->pcie.supports_fw_dump)
|
|
|
+ return;
|
|
|
+
|
|
|
+ for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) {
|
|
|
+ struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
|
|
|
+
|
|
|
+ if (entry->mem_ptr) {
|
|
|
+ vfree(entry->mem_ptr);
|
|
|
+ entry->mem_ptr = NULL;
|
|
|
+ }
|
|
|
+ entry->mem_size = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ do_gettimeofday(&t);
|
|
|
+ dev_info(adapter->dev, "== mwifiex firmware dump start: %u.%06u ==\n",
|
|
|
+ (u32)t.tv_sec, (u32)t.tv_usec);
|
|
|
+
|
|
|
+ /* Read the number of the memories which will dump */
|
|
|
+ stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
|
|
|
+ if (stat == RDWR_STATUS_FAILURE)
|
|
|
+ goto done;
|
|
|
+
|
|
|
+ reg = creg->fw_dump_start;
|
|
|
+ mwifiex_read_reg_byte(adapter, reg, &dump_num);
|
|
|
+
|
|
|
+ /* Read the length of every memory which will dump */
|
|
|
+ for (idx = 0; idx < dump_num; idx++) {
|
|
|
+ struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
|
|
|
+
|
|
|
+ stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
|
|
|
+ if (stat == RDWR_STATUS_FAILURE)
|
|
|
+ goto done;
|
|
|
+
|
|
|
+ memory_size = 0;
|
|
|
+ reg = creg->fw_dump_start;
|
|
|
+ for (i = 0; i < 4; i++) {
|
|
|
+ mwifiex_read_reg_byte(adapter, reg, &read_reg);
|
|
|
+ memory_size |= (read_reg << (i * 8));
|
|
|
+ reg++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (memory_size == 0) {
|
|
|
+ dev_info(adapter->dev, "Firmware dump Finished!\n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ dev_info(adapter->dev,
|
|
|
+ "%s_SIZE=0x%x\n", entry->mem_name, memory_size);
|
|
|
+ entry->mem_ptr = vmalloc(memory_size + 1);
|
|
|
+ entry->mem_size = memory_size;
|
|
|
+ if (!entry->mem_ptr) {
|
|
|
+ dev_err(adapter->dev,
|
|
|
+ "Vmalloc %s failed\n", entry->mem_name);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ dbg_ptr = entry->mem_ptr;
|
|
|
+ end_ptr = dbg_ptr + memory_size;
|
|
|
+
|
|
|
+ doneflag = entry->done_flag;
|
|
|
+ do_gettimeofday(&t);
|
|
|
+ dev_info(adapter->dev, "Start %s output %u.%06u, please wait...\n",
|
|
|
+ entry->mem_name, (u32)t.tv_sec, (u32)t.tv_usec);
|
|
|
+
|
|
|
+ do {
|
|
|
+ stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
|
|
|
+ if (RDWR_STATUS_FAILURE == stat)
|
|
|
+ goto done;
|
|
|
+
|
|
|
+ reg_start = creg->fw_dump_start;
|
|
|
+ reg_end = creg->fw_dump_end;
|
|
|
+ for (reg = reg_start; reg <= reg_end; reg++) {
|
|
|
+ mwifiex_read_reg_byte(adapter, reg, dbg_ptr);
|
|
|
+ if (dbg_ptr < end_ptr)
|
|
|
+ dbg_ptr++;
|
|
|
+ else
|
|
|
+ dev_err(adapter->dev,
|
|
|
+ "Allocated buf not enough\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (stat != RDWR_STATUS_DONE)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ dev_info(adapter->dev, "%s done: size=0x%tx\n",
|
|
|
+ entry->mem_name, dbg_ptr - entry->mem_ptr);
|
|
|
+ break;
|
|
|
+ } while (true);
|
|
|
+ }
|
|
|
+ do_gettimeofday(&t);
|
|
|
+ dev_info(adapter->dev, "== mwifiex firmware dump end: %u.%06u ==\n",
|
|
|
+ (u32)t.tv_sec, (u32)t.tv_usec);
|
|
|
+
|
|
|
+ kobject_uevent_env(&adapter->wiphy->dev.kobj, KOBJ_CHANGE, env);
|
|
|
+
|
|
|
+done:
|
|
|
+ adapter->curr_mem_idx = 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void mwifiex_pcie_work(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct mwifiex_adapter *adapter =
|
|
|
+ container_of(work, struct mwifiex_adapter, iface_work);
|
|
|
+
|
|
|
+ if (test_and_clear_bit(MWIFIEX_IFACE_WORK_FW_DUMP,
|
|
|
+ &adapter->iface_work_flags))
|
|
|
+ mwifiex_pcie_fw_dump_work(adapter);
|
|
|
+}
|
|
|
+
|
|
|
+/* This function dumps FW information */
|
|
|
+static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter)
|
|
|
+{
|
|
|
+ if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags))
|
|
|
+ return;
|
|
|
+
|
|
|
+ set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags);
|
|
|
+
|
|
|
+ schedule_work(&adapter->iface_work);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* This function initializes the PCI-E host memory space, WCB rings, etc.
|
|
|
*
|
|
@@ -2342,6 +2531,8 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
|
|
|
|
|
|
adapter->dev = &pdev->dev;
|
|
|
adapter->tx_buf_size = card->pcie.tx_buf_size;
|
|
|
+ adapter->mem_type_mapping_tbl = mem_type_mapping_tbl;
|
|
|
+ adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl);
|
|
|
strcpy(adapter->fw_name, card->pcie.firmware);
|
|
|
|
|
|
return 0;
|
|
@@ -2394,6 +2585,8 @@ static struct mwifiex_if_ops pcie_ops = {
|
|
|
.cleanup_mpa_buf = NULL,
|
|
|
.init_fw_port = mwifiex_pcie_init_fw_port,
|
|
|
.clean_pcie_ring = mwifiex_clean_pcie_ring_buf,
|
|
|
+ .fw_dump = mwifiex_pcie_fw_dump,
|
|
|
+ .iface_work = mwifiex_pcie_work,
|
|
|
};
|
|
|
|
|
|
/*
|