Loading drivers/bluetooth/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -210,6 +210,7 @@ config BT_MRVL_SDIO tristate "Marvell BT-over-SDIO driver" depends on BT_MRVL && MMC select FW_LOADER select WANT_DEV_COREDUMP help The driver for Marvell Bluetooth chipsets with SDIO interface. Loading drivers/bluetooth/btmrvl_debugfs.c +31 −0 Original line number Diff line number Diff line Loading @@ -167,6 +167,35 @@ static const struct file_operations btmrvl_hscmd_fops = { .llseek = default_llseek, }; static ssize_t btmrvl_fwdump_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; bool result; memset(buf, 0, sizeof(buf)); if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; if (strtobool(buf, &result)) return -EINVAL; if (!result) return -EINVAL; btmrvl_firmware_dump(priv); return count; } static const struct file_operations btmrvl_fwdump_fops = { .write = btmrvl_fwdump_write, .open = simple_open, .llseek = default_llseek, }; void btmrvl_debugfs_init(struct hci_dev *hdev) { struct btmrvl_private *priv = hci_get_drvdata(hdev); Loading Loading @@ -197,6 +226,8 @@ void btmrvl_debugfs_init(struct hci_dev *hdev) priv, &btmrvl_hscmd_fops); debugfs_create_file("hscfgcmd", 0644, dbg->config_dir, priv, &btmrvl_hscfgcmd_fops); debugfs_create_file("fw_dump", 0200, dbg->config_dir, priv, &btmrvl_fwdump_fops); dbg->status_dir = debugfs_create_dir("status", hdev->debugfs); debugfs_create_u8("curpsmode", 0444, dbg->status_dir, Loading drivers/bluetooth/btmrvl_drv.h +20 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,24 @@ /* Time to wait for command response in millisecond */ #define WAIT_UNTIL_CMD_RESP 5000 enum rdwr_status { RDWR_STATUS_SUCCESS = 0, RDWR_STATUS_FAILURE = 1, RDWR_STATUS_DONE = 2 }; #define FW_DUMP_MAX_NAME_LEN 8 #define FW_DUMP_HOST_READY 0xEE #define FW_DUMP_DONE 0xFF #define FW_DUMP_READ_DONE 0xFE struct memory_type_mapping { u8 mem_name[FW_DUMP_MAX_NAME_LEN]; u8 *mem_ptr; u32 mem_size; u8 done_flag; }; struct btmrvl_thread { struct task_struct *task; wait_queue_head_t wait_q; Loading Loading @@ -81,6 +99,7 @@ struct btmrvl_private { u8 *payload, u16 nb); int (*hw_wakeup_firmware) (struct btmrvl_private *priv); int (*hw_process_int_status) (struct btmrvl_private *priv); void (*firmware_dump)(struct btmrvl_private *priv); spinlock_t driver_lock; /* spinlock used by driver */ #ifdef CONFIG_DEBUG_FS void *debugfs_data; Loading Loading @@ -151,6 +170,7 @@ int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv); int btmrvl_enable_ps(struct btmrvl_private *priv); int btmrvl_prepare_command(struct btmrvl_private *priv); int btmrvl_enable_hs(struct btmrvl_private *priv); void btmrvl_firmware_dump(struct btmrvl_private *priv); #ifdef CONFIG_DEBUG_FS void btmrvl_debugfs_init(struct hci_dev *hdev); Loading drivers/bluetooth/btmrvl_main.c +14 −7 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ #include <linux/of.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> #include <linux/mmc/sdio_func.h> #include "btmrvl_drv.h" #include "btmrvl_sdio.h" Loading @@ -42,7 +43,7 @@ void btmrvl_interrupt(struct btmrvl_private *priv) priv->adapter->int_count++; if (priv->adapter->hs_state == HS_ACTIVATED) { BT_DBG("BT: HS DEACTIVATED in ISR!\n"); BT_DBG("BT: HS DEACTIVATED in ISR!"); priv->adapter->hs_state = HS_DEACTIVATED; } Loading Loading @@ -214,7 +215,7 @@ int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd) ret = btmrvl_send_sync_cmd(priv, BT_CMD_MODULE_CFG_REQ, &subcmd, 1); if (ret) BT_ERR("module_cfg_cmd(%x) failed\n", subcmd); BT_ERR("module_cfg_cmd(%x) failed", subcmd); return ret; } Loading Loading @@ -250,7 +251,7 @@ int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv) ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_CONFIG, param, 2); if (ret) BT_ERR("HSCFG command failed\n"); BT_ERR("HSCFG command failed"); return ret; } Loading @@ -268,7 +269,7 @@ int btmrvl_enable_ps(struct btmrvl_private *priv) ret = btmrvl_send_sync_cmd(priv, BT_CMD_AUTO_SLEEP_MODE, ¶m, 1); if (ret) BT_ERR("PSMODE command failed\n"); BT_ERR("PSMODE command failed"); return 0; } Loading @@ -281,7 +282,7 @@ int btmrvl_enable_hs(struct btmrvl_private *priv) ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0); if (ret) { BT_ERR("Host sleep enable command failed\n"); BT_ERR("Host sleep enable command failed"); return ret; } Loading Loading @@ -328,13 +329,19 @@ int btmrvl_prepare_command(struct btmrvl_private *priv) } else { ret = priv->hw_wakeup_firmware(priv); priv->adapter->hs_state = HS_DEACTIVATED; BT_DBG("BT: HS DEACTIVATED due to host activity!\n"); BT_DBG("BT: HS DEACTIVATED due to host activity!"); } } return ret; } void btmrvl_firmware_dump(struct btmrvl_private *priv) { if (priv->firmware_dump) priv->firmware_dump(priv); } static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb) { int ret = 0; Loading Loading @@ -493,7 +500,7 @@ static int btmrvl_download_cal_data(struct btmrvl_private *priv, ret = btmrvl_send_sync_cmd(priv, BT_CMD_LOAD_CONFIG_DATA, data, BT_CAL_HDR_LEN + len); if (ret) BT_ERR("Failed to download caibration data\n"); BT_ERR("Failed to download caibration data"); return 0; } Loading drivers/bluetooth/btmrvl_sdio.c +302 −2 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include <linux/mmc/sdio_ids.h> #include <linux/mmc/sdio_func.h> #include <linux/module.h> #include <linux/devcoredump.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> Loading @@ -33,6 +34,24 @@ #define VERSION "1.0" static struct memory_type_mapping mem_type_mapping_tbl[] = { {"ITCM", NULL, 0, 0xF0}, {"DTCM", NULL, 0, 0xF1}, {"SQRAM", NULL, 0, 0xF2}, {"APU", NULL, 0, 0xF3}, {"CIU", NULL, 0, 0xF4}, {"ICU", NULL, 0, 0xF5}, {"MAC", NULL, 0, 0xF6}, {"EXT7", NULL, 0, 0xF7}, {"EXT8", NULL, 0, 0xF8}, {"EXT9", NULL, 0, 0xF9}, {"EXT10", NULL, 0, 0xFA}, {"EXT11", NULL, 0, 0xFB}, {"EXT12", NULL, 0, 0xFC}, {"EXT13", NULL, 0, 0xFD}, {"EXTLAST", NULL, 0, 0xFE}, }; /* The btmrvl_sdio_remove() callback function is called * when user removes this module from kernel space or ejects * the card from the slot. The driver handles these 2 cases Loading Loading @@ -122,6 +141,9 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = { .int_read_to_clear = true, .host_int_rsr = 0x01, .card_misc_cfg = 0xcc, .fw_dump_ctrl = 0xe2, .fw_dump_start = 0xe3, .fw_dump_end = 0xea, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { Loading @@ -130,6 +152,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { .reg = &btmrvl_reg_8688, .support_pscan_win_report = false, .sd_blksz_fw_dl = 64, .supports_fw_dump = false, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { Loading @@ -138,6 +161,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { .reg = &btmrvl_reg_87xx, .support_pscan_win_report = false, .sd_blksz_fw_dl = 256, .supports_fw_dump = false, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = { Loading @@ -146,6 +170,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = { .reg = &btmrvl_reg_87xx, .support_pscan_win_report = false, .sd_blksz_fw_dl = 256, .supports_fw_dump = false, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = { Loading @@ -154,6 +179,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = { .reg = &btmrvl_reg_8887, .support_pscan_win_report = true, .sd_blksz_fw_dl = 256, .supports_fw_dump = false, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = { Loading @@ -162,6 +188,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = { .reg = &btmrvl_reg_8897, .support_pscan_win_report = true, .sd_blksz_fw_dl = 256, .supports_fw_dump = true, }; static const struct sdio_device_id btmrvl_sdio_ids[] = { Loading Loading @@ -764,8 +791,8 @@ static void btmrvl_sdio_interrupt(struct sdio_func *func) card = sdio_get_drvdata(func); if (!card || !card->priv) { BT_ERR("sbi_interrupt(%p) card or priv is " "NULL, card=%p\n", func, card); BT_ERR("sbi_interrupt(%p) card or priv is NULL, card=%p", func, card); return; } Loading Loading @@ -1080,6 +1107,277 @@ static int btmrvl_sdio_wakeup_fw(struct btmrvl_private *priv) return ret; } static void btmrvl_sdio_dump_regs(struct btmrvl_private *priv) { struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; int ret = 0; unsigned int reg, reg_start, reg_end; char buf[256], *ptr; u8 loop, func, data; int MAX_LOOP = 2; btmrvl_sdio_wakeup_fw(priv); sdio_claim_host(card->func); for (loop = 0; loop < MAX_LOOP; loop++) { memset(buf, 0, sizeof(buf)); ptr = buf; if (loop == 0) { /* Read the registers of SDIO function0 */ func = loop; reg_start = 0; reg_end = 9; } else { func = 2; reg_start = 0; reg_end = 0x09; } ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func, reg_start, reg_end); for (reg = reg_start; reg <= reg_end; reg++) { if (func == 0) data = sdio_f0_readb(card->func, reg, &ret); else data = sdio_readb(card->func, reg, &ret); if (!ret) { ptr += sprintf(ptr, "%02x ", data); } else { ptr += sprintf(ptr, "ERR"); break; } } BT_INFO("%s", buf); } sdio_release_host(card->func); } /* This function read/write firmware */ static enum rdwr_status btmrvl_sdio_rdwr_firmware(struct btmrvl_private *priv, u8 doneflag) { struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; int ret, tries; u8 ctrl_data = 0; sdio_writeb(card->func, FW_DUMP_HOST_READY, card->reg->fw_dump_ctrl, &ret); if (ret) { BT_ERR("SDIO write err"); return RDWR_STATUS_FAILURE; } for (tries = 0; tries < MAX_POLL_TRIES; tries++) { ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl, &ret); if (ret) { BT_ERR("SDIO read err"); return RDWR_STATUS_FAILURE; } if (ctrl_data == FW_DUMP_DONE) break; if (doneflag && ctrl_data == doneflag) return RDWR_STATUS_DONE; if (ctrl_data != FW_DUMP_HOST_READY) { BT_INFO("The ctrl reg was changed, re-try again!"); sdio_writeb(card->func, FW_DUMP_HOST_READY, card->reg->fw_dump_ctrl, &ret); if (ret) { BT_ERR("SDIO write err"); return RDWR_STATUS_FAILURE; } } usleep_range(100, 200); } if (ctrl_data == FW_DUMP_HOST_READY) { BT_ERR("Fail to pull ctrl_data"); return RDWR_STATUS_FAILURE; } return RDWR_STATUS_SUCCESS; } /* This function dump sdio register and memory data */ static void btmrvl_sdio_dump_firmware(struct btmrvl_private *priv) { struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; int ret = 0; unsigned int reg, reg_start, reg_end; enum rdwr_status stat; u8 *dbg_ptr, *end_ptr, *fw_dump_data, *fw_dump_ptr; u8 dump_num, idx, i, read_reg, doneflag = 0; u32 memory_size, fw_dump_len = 0; /* dump sdio register first */ btmrvl_sdio_dump_regs(priv); if (!card->supports_fw_dump) { BT_ERR("Firmware dump not supported for this card!"); 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; } btmrvl_sdio_wakeup_fw(priv); sdio_claim_host(card->func); BT_INFO("== btmrvl firmware dump start =="); stat = btmrvl_sdio_rdwr_firmware(priv, doneflag); if (stat == RDWR_STATUS_FAILURE) goto done; reg = card->reg->fw_dump_start; /* Read the number of the memories which will dump */ dump_num = sdio_readb(card->func, reg, &ret); if (ret) { BT_ERR("SDIO read memory length err"); goto done; } /* 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 = btmrvl_sdio_rdwr_firmware(priv, doneflag); if (stat == RDWR_STATUS_FAILURE) goto done; memory_size = 0; reg = card->reg->fw_dump_start; for (i = 0; i < 4; i++) { read_reg = sdio_readb(card->func, reg, &ret); if (ret) { BT_ERR("SDIO read err"); goto done; } memory_size |= (read_reg << i*8); reg++; } if (memory_size == 0) { BT_INFO("Firmware dump finished!"); break; } BT_INFO("%s_SIZE=0x%x", entry->mem_name, memory_size); entry->mem_ptr = vzalloc(memory_size + 1); entry->mem_size = memory_size; if (!entry->mem_ptr) { BT_ERR("Vzalloc %s failed", entry->mem_name); goto done; } fw_dump_len += (strlen("========Start dump ") + strlen(entry->mem_name) + strlen("========\n") + (memory_size + 1) + strlen("\n========End dump========\n")); dbg_ptr = entry->mem_ptr; end_ptr = dbg_ptr + memory_size; doneflag = entry->done_flag; BT_INFO("Start %s output, please wait...", entry->mem_name); do { stat = btmrvl_sdio_rdwr_firmware(priv, doneflag); if (stat == RDWR_STATUS_FAILURE) goto done; reg_start = card->reg->fw_dump_start; reg_end = card->reg->fw_dump_end; for (reg = reg_start; reg <= reg_end; reg++) { *dbg_ptr = sdio_readb(card->func, reg, &ret); if (ret) { BT_ERR("SDIO read err"); goto done; } if (dbg_ptr < end_ptr) dbg_ptr++; else BT_ERR("Allocated buffer not enough"); } if (stat != RDWR_STATUS_DONE) { continue; } else { BT_INFO("%s done: size=0x%tx", entry->mem_name, dbg_ptr - entry->mem_ptr); break; } } while (1); } BT_INFO("== btmrvl firmware dump end =="); done: sdio_release_host(card->func); if (fw_dump_len == 0) return; fw_dump_data = vzalloc(fw_dump_len+1); if (!fw_dump_data) { BT_ERR("Vzalloc fw_dump_data fail!"); return; } fw_dump_ptr = fw_dump_data; /* Dump all the memory data into single file, a userspace script will be used to split all the memory data to multiple files*/ BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump start"); for (idx = 0; idx < dump_num; idx++) { struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; if (entry->mem_ptr) { strcpy(fw_dump_ptr, "========Start dump "); fw_dump_ptr += strlen("========Start dump "); strcpy(fw_dump_ptr, entry->mem_name); fw_dump_ptr += strlen(entry->mem_name); strcpy(fw_dump_ptr, "========\n"); fw_dump_ptr += strlen("========\n"); memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size); fw_dump_ptr += entry->mem_size; strcpy(fw_dump_ptr, "\n========End dump========\n"); fw_dump_ptr += strlen("\n========End dump========\n"); vfree(mem_type_mapping_tbl[idx].mem_ptr); mem_type_mapping_tbl[idx].mem_ptr = NULL; } } /* fw_dump_data will be free in device coredump release function after 5 min*/ dev_coredumpv(&priv->btmrvl_dev.hcidev->dev, fw_dump_data, fw_dump_len, GFP_KERNEL); BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump end"); } static int btmrvl_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { Loading @@ -1103,6 +1401,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func, card->reg = data->reg; card->sd_blksz_fw_dl = data->sd_blksz_fw_dl; card->support_pscan_win_report = data->support_pscan_win_report; card->supports_fw_dump = data->supports_fw_dump; } if (btmrvl_sdio_register_dev(card) < 0) { Loading Loading @@ -1134,6 +1433,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func, priv->hw_host_to_card = btmrvl_sdio_host_to_card; priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw; priv->hw_process_int_status = btmrvl_sdio_process_int_status; priv->firmware_dump = btmrvl_sdio_dump_firmware; if (btmrvl_register_hdev(priv)) { BT_ERR("Register hdev failed!"); Loading Loading
drivers/bluetooth/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -210,6 +210,7 @@ config BT_MRVL_SDIO tristate "Marvell BT-over-SDIO driver" depends on BT_MRVL && MMC select FW_LOADER select WANT_DEV_COREDUMP help The driver for Marvell Bluetooth chipsets with SDIO interface. Loading
drivers/bluetooth/btmrvl_debugfs.c +31 −0 Original line number Diff line number Diff line Loading @@ -167,6 +167,35 @@ static const struct file_operations btmrvl_hscmd_fops = { .llseek = default_llseek, }; static ssize_t btmrvl_fwdump_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { struct btmrvl_private *priv = file->private_data; char buf[16]; bool result; memset(buf, 0, sizeof(buf)); if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; if (strtobool(buf, &result)) return -EINVAL; if (!result) return -EINVAL; btmrvl_firmware_dump(priv); return count; } static const struct file_operations btmrvl_fwdump_fops = { .write = btmrvl_fwdump_write, .open = simple_open, .llseek = default_llseek, }; void btmrvl_debugfs_init(struct hci_dev *hdev) { struct btmrvl_private *priv = hci_get_drvdata(hdev); Loading Loading @@ -197,6 +226,8 @@ void btmrvl_debugfs_init(struct hci_dev *hdev) priv, &btmrvl_hscmd_fops); debugfs_create_file("hscfgcmd", 0644, dbg->config_dir, priv, &btmrvl_hscfgcmd_fops); debugfs_create_file("fw_dump", 0200, dbg->config_dir, priv, &btmrvl_fwdump_fops); dbg->status_dir = debugfs_create_dir("status", hdev->debugfs); debugfs_create_u8("curpsmode", 0444, dbg->status_dir, Loading
drivers/bluetooth/btmrvl_drv.h +20 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,24 @@ /* Time to wait for command response in millisecond */ #define WAIT_UNTIL_CMD_RESP 5000 enum rdwr_status { RDWR_STATUS_SUCCESS = 0, RDWR_STATUS_FAILURE = 1, RDWR_STATUS_DONE = 2 }; #define FW_DUMP_MAX_NAME_LEN 8 #define FW_DUMP_HOST_READY 0xEE #define FW_DUMP_DONE 0xFF #define FW_DUMP_READ_DONE 0xFE struct memory_type_mapping { u8 mem_name[FW_DUMP_MAX_NAME_LEN]; u8 *mem_ptr; u32 mem_size; u8 done_flag; }; struct btmrvl_thread { struct task_struct *task; wait_queue_head_t wait_q; Loading Loading @@ -81,6 +99,7 @@ struct btmrvl_private { u8 *payload, u16 nb); int (*hw_wakeup_firmware) (struct btmrvl_private *priv); int (*hw_process_int_status) (struct btmrvl_private *priv); void (*firmware_dump)(struct btmrvl_private *priv); spinlock_t driver_lock; /* spinlock used by driver */ #ifdef CONFIG_DEBUG_FS void *debugfs_data; Loading Loading @@ -151,6 +170,7 @@ int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv); int btmrvl_enable_ps(struct btmrvl_private *priv); int btmrvl_prepare_command(struct btmrvl_private *priv); int btmrvl_enable_hs(struct btmrvl_private *priv); void btmrvl_firmware_dump(struct btmrvl_private *priv); #ifdef CONFIG_DEBUG_FS void btmrvl_debugfs_init(struct hci_dev *hdev); Loading
drivers/bluetooth/btmrvl_main.c +14 −7 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ #include <linux/of.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> #include <linux/mmc/sdio_func.h> #include "btmrvl_drv.h" #include "btmrvl_sdio.h" Loading @@ -42,7 +43,7 @@ void btmrvl_interrupt(struct btmrvl_private *priv) priv->adapter->int_count++; if (priv->adapter->hs_state == HS_ACTIVATED) { BT_DBG("BT: HS DEACTIVATED in ISR!\n"); BT_DBG("BT: HS DEACTIVATED in ISR!"); priv->adapter->hs_state = HS_DEACTIVATED; } Loading Loading @@ -214,7 +215,7 @@ int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd) ret = btmrvl_send_sync_cmd(priv, BT_CMD_MODULE_CFG_REQ, &subcmd, 1); if (ret) BT_ERR("module_cfg_cmd(%x) failed\n", subcmd); BT_ERR("module_cfg_cmd(%x) failed", subcmd); return ret; } Loading Loading @@ -250,7 +251,7 @@ int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv) ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_CONFIG, param, 2); if (ret) BT_ERR("HSCFG command failed\n"); BT_ERR("HSCFG command failed"); return ret; } Loading @@ -268,7 +269,7 @@ int btmrvl_enable_ps(struct btmrvl_private *priv) ret = btmrvl_send_sync_cmd(priv, BT_CMD_AUTO_SLEEP_MODE, ¶m, 1); if (ret) BT_ERR("PSMODE command failed\n"); BT_ERR("PSMODE command failed"); return 0; } Loading @@ -281,7 +282,7 @@ int btmrvl_enable_hs(struct btmrvl_private *priv) ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0); if (ret) { BT_ERR("Host sleep enable command failed\n"); BT_ERR("Host sleep enable command failed"); return ret; } Loading Loading @@ -328,13 +329,19 @@ int btmrvl_prepare_command(struct btmrvl_private *priv) } else { ret = priv->hw_wakeup_firmware(priv); priv->adapter->hs_state = HS_DEACTIVATED; BT_DBG("BT: HS DEACTIVATED due to host activity!\n"); BT_DBG("BT: HS DEACTIVATED due to host activity!"); } } return ret; } void btmrvl_firmware_dump(struct btmrvl_private *priv) { if (priv->firmware_dump) priv->firmware_dump(priv); } static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb) { int ret = 0; Loading Loading @@ -493,7 +500,7 @@ static int btmrvl_download_cal_data(struct btmrvl_private *priv, ret = btmrvl_send_sync_cmd(priv, BT_CMD_LOAD_CONFIG_DATA, data, BT_CAL_HDR_LEN + len); if (ret) BT_ERR("Failed to download caibration data\n"); BT_ERR("Failed to download caibration data"); return 0; } Loading
drivers/bluetooth/btmrvl_sdio.c +302 −2 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include <linux/mmc/sdio_ids.h> #include <linux/mmc/sdio_func.h> #include <linux/module.h> #include <linux/devcoredump.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> Loading @@ -33,6 +34,24 @@ #define VERSION "1.0" static struct memory_type_mapping mem_type_mapping_tbl[] = { {"ITCM", NULL, 0, 0xF0}, {"DTCM", NULL, 0, 0xF1}, {"SQRAM", NULL, 0, 0xF2}, {"APU", NULL, 0, 0xF3}, {"CIU", NULL, 0, 0xF4}, {"ICU", NULL, 0, 0xF5}, {"MAC", NULL, 0, 0xF6}, {"EXT7", NULL, 0, 0xF7}, {"EXT8", NULL, 0, 0xF8}, {"EXT9", NULL, 0, 0xF9}, {"EXT10", NULL, 0, 0xFA}, {"EXT11", NULL, 0, 0xFB}, {"EXT12", NULL, 0, 0xFC}, {"EXT13", NULL, 0, 0xFD}, {"EXTLAST", NULL, 0, 0xFE}, }; /* The btmrvl_sdio_remove() callback function is called * when user removes this module from kernel space or ejects * the card from the slot. The driver handles these 2 cases Loading Loading @@ -122,6 +141,9 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = { .int_read_to_clear = true, .host_int_rsr = 0x01, .card_misc_cfg = 0xcc, .fw_dump_ctrl = 0xe2, .fw_dump_start = 0xe3, .fw_dump_end = 0xea, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { Loading @@ -130,6 +152,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { .reg = &btmrvl_reg_8688, .support_pscan_win_report = false, .sd_blksz_fw_dl = 64, .supports_fw_dump = false, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { Loading @@ -138,6 +161,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { .reg = &btmrvl_reg_87xx, .support_pscan_win_report = false, .sd_blksz_fw_dl = 256, .supports_fw_dump = false, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = { Loading @@ -146,6 +170,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = { .reg = &btmrvl_reg_87xx, .support_pscan_win_report = false, .sd_blksz_fw_dl = 256, .supports_fw_dump = false, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = { Loading @@ -154,6 +179,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = { .reg = &btmrvl_reg_8887, .support_pscan_win_report = true, .sd_blksz_fw_dl = 256, .supports_fw_dump = false, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = { Loading @@ -162,6 +188,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = { .reg = &btmrvl_reg_8897, .support_pscan_win_report = true, .sd_blksz_fw_dl = 256, .supports_fw_dump = true, }; static const struct sdio_device_id btmrvl_sdio_ids[] = { Loading Loading @@ -764,8 +791,8 @@ static void btmrvl_sdio_interrupt(struct sdio_func *func) card = sdio_get_drvdata(func); if (!card || !card->priv) { BT_ERR("sbi_interrupt(%p) card or priv is " "NULL, card=%p\n", func, card); BT_ERR("sbi_interrupt(%p) card or priv is NULL, card=%p", func, card); return; } Loading Loading @@ -1080,6 +1107,277 @@ static int btmrvl_sdio_wakeup_fw(struct btmrvl_private *priv) return ret; } static void btmrvl_sdio_dump_regs(struct btmrvl_private *priv) { struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; int ret = 0; unsigned int reg, reg_start, reg_end; char buf[256], *ptr; u8 loop, func, data; int MAX_LOOP = 2; btmrvl_sdio_wakeup_fw(priv); sdio_claim_host(card->func); for (loop = 0; loop < MAX_LOOP; loop++) { memset(buf, 0, sizeof(buf)); ptr = buf; if (loop == 0) { /* Read the registers of SDIO function0 */ func = loop; reg_start = 0; reg_end = 9; } else { func = 2; reg_start = 0; reg_end = 0x09; } ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func, reg_start, reg_end); for (reg = reg_start; reg <= reg_end; reg++) { if (func == 0) data = sdio_f0_readb(card->func, reg, &ret); else data = sdio_readb(card->func, reg, &ret); if (!ret) { ptr += sprintf(ptr, "%02x ", data); } else { ptr += sprintf(ptr, "ERR"); break; } } BT_INFO("%s", buf); } sdio_release_host(card->func); } /* This function read/write firmware */ static enum rdwr_status btmrvl_sdio_rdwr_firmware(struct btmrvl_private *priv, u8 doneflag) { struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; int ret, tries; u8 ctrl_data = 0; sdio_writeb(card->func, FW_DUMP_HOST_READY, card->reg->fw_dump_ctrl, &ret); if (ret) { BT_ERR("SDIO write err"); return RDWR_STATUS_FAILURE; } for (tries = 0; tries < MAX_POLL_TRIES; tries++) { ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl, &ret); if (ret) { BT_ERR("SDIO read err"); return RDWR_STATUS_FAILURE; } if (ctrl_data == FW_DUMP_DONE) break; if (doneflag && ctrl_data == doneflag) return RDWR_STATUS_DONE; if (ctrl_data != FW_DUMP_HOST_READY) { BT_INFO("The ctrl reg was changed, re-try again!"); sdio_writeb(card->func, FW_DUMP_HOST_READY, card->reg->fw_dump_ctrl, &ret); if (ret) { BT_ERR("SDIO write err"); return RDWR_STATUS_FAILURE; } } usleep_range(100, 200); } if (ctrl_data == FW_DUMP_HOST_READY) { BT_ERR("Fail to pull ctrl_data"); return RDWR_STATUS_FAILURE; } return RDWR_STATUS_SUCCESS; } /* This function dump sdio register and memory data */ static void btmrvl_sdio_dump_firmware(struct btmrvl_private *priv) { struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; int ret = 0; unsigned int reg, reg_start, reg_end; enum rdwr_status stat; u8 *dbg_ptr, *end_ptr, *fw_dump_data, *fw_dump_ptr; u8 dump_num, idx, i, read_reg, doneflag = 0; u32 memory_size, fw_dump_len = 0; /* dump sdio register first */ btmrvl_sdio_dump_regs(priv); if (!card->supports_fw_dump) { BT_ERR("Firmware dump not supported for this card!"); 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; } btmrvl_sdio_wakeup_fw(priv); sdio_claim_host(card->func); BT_INFO("== btmrvl firmware dump start =="); stat = btmrvl_sdio_rdwr_firmware(priv, doneflag); if (stat == RDWR_STATUS_FAILURE) goto done; reg = card->reg->fw_dump_start; /* Read the number of the memories which will dump */ dump_num = sdio_readb(card->func, reg, &ret); if (ret) { BT_ERR("SDIO read memory length err"); goto done; } /* 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 = btmrvl_sdio_rdwr_firmware(priv, doneflag); if (stat == RDWR_STATUS_FAILURE) goto done; memory_size = 0; reg = card->reg->fw_dump_start; for (i = 0; i < 4; i++) { read_reg = sdio_readb(card->func, reg, &ret); if (ret) { BT_ERR("SDIO read err"); goto done; } memory_size |= (read_reg << i*8); reg++; } if (memory_size == 0) { BT_INFO("Firmware dump finished!"); break; } BT_INFO("%s_SIZE=0x%x", entry->mem_name, memory_size); entry->mem_ptr = vzalloc(memory_size + 1); entry->mem_size = memory_size; if (!entry->mem_ptr) { BT_ERR("Vzalloc %s failed", entry->mem_name); goto done; } fw_dump_len += (strlen("========Start dump ") + strlen(entry->mem_name) + strlen("========\n") + (memory_size + 1) + strlen("\n========End dump========\n")); dbg_ptr = entry->mem_ptr; end_ptr = dbg_ptr + memory_size; doneflag = entry->done_flag; BT_INFO("Start %s output, please wait...", entry->mem_name); do { stat = btmrvl_sdio_rdwr_firmware(priv, doneflag); if (stat == RDWR_STATUS_FAILURE) goto done; reg_start = card->reg->fw_dump_start; reg_end = card->reg->fw_dump_end; for (reg = reg_start; reg <= reg_end; reg++) { *dbg_ptr = sdio_readb(card->func, reg, &ret); if (ret) { BT_ERR("SDIO read err"); goto done; } if (dbg_ptr < end_ptr) dbg_ptr++; else BT_ERR("Allocated buffer not enough"); } if (stat != RDWR_STATUS_DONE) { continue; } else { BT_INFO("%s done: size=0x%tx", entry->mem_name, dbg_ptr - entry->mem_ptr); break; } } while (1); } BT_INFO("== btmrvl firmware dump end =="); done: sdio_release_host(card->func); if (fw_dump_len == 0) return; fw_dump_data = vzalloc(fw_dump_len+1); if (!fw_dump_data) { BT_ERR("Vzalloc fw_dump_data fail!"); return; } fw_dump_ptr = fw_dump_data; /* Dump all the memory data into single file, a userspace script will be used to split all the memory data to multiple files*/ BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump start"); for (idx = 0; idx < dump_num; idx++) { struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; if (entry->mem_ptr) { strcpy(fw_dump_ptr, "========Start dump "); fw_dump_ptr += strlen("========Start dump "); strcpy(fw_dump_ptr, entry->mem_name); fw_dump_ptr += strlen(entry->mem_name); strcpy(fw_dump_ptr, "========\n"); fw_dump_ptr += strlen("========\n"); memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size); fw_dump_ptr += entry->mem_size; strcpy(fw_dump_ptr, "\n========End dump========\n"); fw_dump_ptr += strlen("\n========End dump========\n"); vfree(mem_type_mapping_tbl[idx].mem_ptr); mem_type_mapping_tbl[idx].mem_ptr = NULL; } } /* fw_dump_data will be free in device coredump release function after 5 min*/ dev_coredumpv(&priv->btmrvl_dev.hcidev->dev, fw_dump_data, fw_dump_len, GFP_KERNEL); BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump end"); } static int btmrvl_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { Loading @@ -1103,6 +1401,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func, card->reg = data->reg; card->sd_blksz_fw_dl = data->sd_blksz_fw_dl; card->support_pscan_win_report = data->support_pscan_win_report; card->supports_fw_dump = data->supports_fw_dump; } if (btmrvl_sdio_register_dev(card) < 0) { Loading Loading @@ -1134,6 +1433,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func, priv->hw_host_to_card = btmrvl_sdio_host_to_card; priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw; priv->hw_process_int_status = btmrvl_sdio_process_int_status; priv->firmware_dump = btmrvl_sdio_dump_firmware; if (btmrvl_register_hdev(priv)) { BT_ERR("Register hdev failed!"); Loading