aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/bluetooth
diff options
context:
space:
mode:
authorXinming Hu <huxm@marvell.com>2014-11-24 05:40:53 -0500
committerMarcel Holtmann <marcel@holtmann.org>2014-12-03 11:35:51 -0500
commitdc759613b0247eb1658d3992f50ba3fad5b61d31 (patch)
tree5ae91a8d93f48204c9c3658b2eba00594c2fb689 /drivers/bluetooth
parent7365d475bf6a0e2497ac4ef29474fffb91d024f1 (diff)
Bluetooth: btmrvl add firmware dump support
This patch adds firmware dump support for marvell bluetooth chipset. Currently only SD8897 is supported. This is implemented based on dev_coredump, a new mechnism introduced in kernel 3.18rc3 Firmware dump can be trigger by echo 1 > /sys/kernel/debug/bluetooth/hci*/config/fw_dump and when the dump operation is completed, data can be read by cat /sys/class/devcoredump/devcd*/data We have prepared following script to divide fw memory dump data into multiple files based on memory type. [root]# cat btmrvl_split_dump_data.sh #!/bin/bash # usage: ./btmrvl_split_dump_data.sh dump_data fw_dump_data=$1 mem_type="ITCM DTCM SQRAM APU CIU ICU MAC EXT7 EXT8 EXT9 EXT10 EXT11 EXT12 EXT13 EXTLAST" for name in ${mem_type[@]} do sed -n "/Start dump $name/,/End dump/p" $fw_dump_data > tmp.$name.log if [ ! -s tmp.$name.log ] then rm -rf tmp.$name.log else # Remove the describle info "Start dump" and "End dump" sed '1d' tmp.$name.log | sed '$d' > /data/$name.log if [ -s /data/$name.log ] then echo "generate /data/$name.log" else sed '1d' tmp.$name.log | sed '$d' > /var/$name.log echo "generate /var/$name.log" fi rm -rf tmp.$name.log fi done Signed-off-by: Xinming Hu <huxm@marvell.com> Signed-off-by: Cathy Luo <cluo@marvell.com> Signed-off-by: Avinash Patil <patila@marvell.com> Reviewed-by: Johannes Berg <johannes@sipsolutions.net> Reviewed-by: Marcel Holtmann <marcel@holtmann.org> Signed-off-by: Amitkumar Karwar <akarwar@marvell.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'drivers/bluetooth')
-rw-r--r--drivers/bluetooth/Kconfig1
-rw-r--r--drivers/bluetooth/btmrvl_debugfs.c31
-rw-r--r--drivers/bluetooth/btmrvl_drv.h20
-rw-r--r--drivers/bluetooth/btmrvl_main.c7
-rw-r--r--drivers/bluetooth/btmrvl_sdio.c300
-rw-r--r--drivers/bluetooth/btmrvl_sdio.h5
6 files changed, 364 insertions, 0 deletions
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 4547dc238fc7..364f080768d0 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -210,6 +210,7 @@ config BT_MRVL_SDIO
210 tristate "Marvell BT-over-SDIO driver" 210 tristate "Marvell BT-over-SDIO driver"
211 depends on BT_MRVL && MMC 211 depends on BT_MRVL && MMC
212 select FW_LOADER 212 select FW_LOADER
213 select WANT_DEV_COREDUMP
213 help 214 help
214 The driver for Marvell Bluetooth chipsets with SDIO interface. 215 The driver for Marvell Bluetooth chipsets with SDIO interface.
215 216
diff --git a/drivers/bluetooth/btmrvl_debugfs.c b/drivers/bluetooth/btmrvl_debugfs.c
index 023d35e3c7a7..1828ed8cae7a 100644
--- a/drivers/bluetooth/btmrvl_debugfs.c
+++ b/drivers/bluetooth/btmrvl_debugfs.c
@@ -167,6 +167,35 @@ static const struct file_operations btmrvl_hscmd_fops = {
167 .llseek = default_llseek, 167 .llseek = default_llseek,
168}; 168};
169 169
170static ssize_t btmrvl_fwdump_write(struct file *file, const char __user *ubuf,
171 size_t count, loff_t *ppos)
172{
173 struct btmrvl_private *priv = file->private_data;
174 char buf[16];
175 bool result;
176
177 memset(buf, 0, sizeof(buf));
178
179 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
180 return -EFAULT;
181
182 if (strtobool(buf, &result))
183 return -EINVAL;
184
185 if (!result)
186 return -EINVAL;
187
188 btmrvl_firmware_dump(priv);
189
190 return count;
191}
192
193static const struct file_operations btmrvl_fwdump_fops = {
194 .write = btmrvl_fwdump_write,
195 .open = simple_open,
196 .llseek = default_llseek,
197};
198
170void btmrvl_debugfs_init(struct hci_dev *hdev) 199void btmrvl_debugfs_init(struct hci_dev *hdev)
171{ 200{
172 struct btmrvl_private *priv = hci_get_drvdata(hdev); 201 struct btmrvl_private *priv = hci_get_drvdata(hdev);
@@ -197,6 +226,8 @@ void btmrvl_debugfs_init(struct hci_dev *hdev)
197 priv, &btmrvl_hscmd_fops); 226 priv, &btmrvl_hscmd_fops);
198 debugfs_create_file("hscfgcmd", 0644, dbg->config_dir, 227 debugfs_create_file("hscfgcmd", 0644, dbg->config_dir,
199 priv, &btmrvl_hscfgcmd_fops); 228 priv, &btmrvl_hscfgcmd_fops);
229 debugfs_create_file("fw_dump", 0200, dbg->config_dir,
230 priv, &btmrvl_fwdump_fops);
200 231
201 dbg->status_dir = debugfs_create_dir("status", hdev->debugfs); 232 dbg->status_dir = debugfs_create_dir("status", hdev->debugfs);
202 debugfs_create_u8("curpsmode", 0444, dbg->status_dir, 233 debugfs_create_u8("curpsmode", 0444, dbg->status_dir,
diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h
index 38ad66289ad6..330f8f84928d 100644
--- a/drivers/bluetooth/btmrvl_drv.h
+++ b/drivers/bluetooth/btmrvl_drv.h
@@ -32,6 +32,24 @@
32/* Time to wait for command response in millisecond */ 32/* Time to wait for command response in millisecond */
33#define WAIT_UNTIL_CMD_RESP 5000 33#define WAIT_UNTIL_CMD_RESP 5000
34 34
35enum rdwr_status {
36 RDWR_STATUS_SUCCESS = 0,
37 RDWR_STATUS_FAILURE = 1,
38 RDWR_STATUS_DONE = 2
39};
40
41#define FW_DUMP_MAX_NAME_LEN 8
42#define FW_DUMP_HOST_READY 0xEE
43#define FW_DUMP_DONE 0xFF
44#define FW_DUMP_READ_DONE 0xFE
45
46struct memory_type_mapping {
47 u8 mem_name[FW_DUMP_MAX_NAME_LEN];
48 u8 *mem_ptr;
49 u32 mem_size;
50 u8 done_flag;
51};
52
35struct btmrvl_thread { 53struct btmrvl_thread {
36 struct task_struct *task; 54 struct task_struct *task;
37 wait_queue_head_t wait_q; 55 wait_queue_head_t wait_q;
@@ -81,6 +99,7 @@ struct btmrvl_private {
81 u8 *payload, u16 nb); 99 u8 *payload, u16 nb);
82 int (*hw_wakeup_firmware) (struct btmrvl_private *priv); 100 int (*hw_wakeup_firmware) (struct btmrvl_private *priv);
83 int (*hw_process_int_status) (struct btmrvl_private *priv); 101 int (*hw_process_int_status) (struct btmrvl_private *priv);
102 void (*firmware_dump)(struct btmrvl_private *priv);
84 spinlock_t driver_lock; /* spinlock used by driver */ 103 spinlock_t driver_lock; /* spinlock used by driver */
85#ifdef CONFIG_DEBUG_FS 104#ifdef CONFIG_DEBUG_FS
86 void *debugfs_data; 105 void *debugfs_data;
@@ -151,6 +170,7 @@ int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv);
151int btmrvl_enable_ps(struct btmrvl_private *priv); 170int btmrvl_enable_ps(struct btmrvl_private *priv);
152int btmrvl_prepare_command(struct btmrvl_private *priv); 171int btmrvl_prepare_command(struct btmrvl_private *priv);
153int btmrvl_enable_hs(struct btmrvl_private *priv); 172int btmrvl_enable_hs(struct btmrvl_private *priv);
173void btmrvl_firmware_dump(struct btmrvl_private *priv);
154 174
155#ifdef CONFIG_DEBUG_FS 175#ifdef CONFIG_DEBUG_FS
156void btmrvl_debugfs_init(struct hci_dev *hdev); 176void btmrvl_debugfs_init(struct hci_dev *hdev);
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c
index 10973ac03fc9..30939c993d94 100644
--- a/drivers/bluetooth/btmrvl_main.c
+++ b/drivers/bluetooth/btmrvl_main.c
@@ -22,6 +22,7 @@
22#include <linux/of.h> 22#include <linux/of.h>
23#include <net/bluetooth/bluetooth.h> 23#include <net/bluetooth/bluetooth.h>
24#include <net/bluetooth/hci_core.h> 24#include <net/bluetooth/hci_core.h>
25#include <linux/mmc/sdio_func.h>
25 26
26#include "btmrvl_drv.h" 27#include "btmrvl_drv.h"
27#include "btmrvl_sdio.h" 28#include "btmrvl_sdio.h"
@@ -335,6 +336,12 @@ int btmrvl_prepare_command(struct btmrvl_private *priv)
335 return ret; 336 return ret;
336} 337}
337 338
339void btmrvl_firmware_dump(struct btmrvl_private *priv)
340{
341 if (priv->firmware_dump)
342 priv->firmware_dump(priv);
343}
344
338static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb) 345static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
339{ 346{
340 int ret = 0; 347 int ret = 0;
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c
index 416d792176c2..0057c0b7a776 100644
--- a/drivers/bluetooth/btmrvl_sdio.c
+++ b/drivers/bluetooth/btmrvl_sdio.c
@@ -24,6 +24,7 @@
24#include <linux/mmc/sdio_ids.h> 24#include <linux/mmc/sdio_ids.h>
25#include <linux/mmc/sdio_func.h> 25#include <linux/mmc/sdio_func.h>
26#include <linux/module.h> 26#include <linux/module.h>
27#include <linux/devcoredump.h>
27 28
28#include <net/bluetooth/bluetooth.h> 29#include <net/bluetooth/bluetooth.h>
29#include <net/bluetooth/hci_core.h> 30#include <net/bluetooth/hci_core.h>
@@ -33,6 +34,24 @@
33 34
34#define VERSION "1.0" 35#define VERSION "1.0"
35 36
37static struct memory_type_mapping mem_type_mapping_tbl[] = {
38 {"ITCM", NULL, 0, 0xF0},
39 {"DTCM", NULL, 0, 0xF1},
40 {"SQRAM", NULL, 0, 0xF2},
41 {"APU", NULL, 0, 0xF3},
42 {"CIU", NULL, 0, 0xF4},
43 {"ICU", NULL, 0, 0xF5},
44 {"MAC", NULL, 0, 0xF6},
45 {"EXT7", NULL, 0, 0xF7},
46 {"EXT8", NULL, 0, 0xF8},
47 {"EXT9", NULL, 0, 0xF9},
48 {"EXT10", NULL, 0, 0xFA},
49 {"EXT11", NULL, 0, 0xFB},
50 {"EXT12", NULL, 0, 0xFC},
51 {"EXT13", NULL, 0, 0xFD},
52 {"EXTLAST", NULL, 0, 0xFE},
53};
54
36/* The btmrvl_sdio_remove() callback function is called 55/* The btmrvl_sdio_remove() callback function is called
37 * when user removes this module from kernel space or ejects 56 * when user removes this module from kernel space or ejects
38 * the card from the slot. The driver handles these 2 cases 57 * the card from the slot. The driver handles these 2 cases
@@ -122,6 +141,9 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = {
122 .int_read_to_clear = true, 141 .int_read_to_clear = true,
123 .host_int_rsr = 0x01, 142 .host_int_rsr = 0x01,
124 .card_misc_cfg = 0xcc, 143 .card_misc_cfg = 0xcc,
144 .fw_dump_ctrl = 0xe2,
145 .fw_dump_start = 0xe3,
146 .fw_dump_end = 0xea,
125}; 147};
126 148
127static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { 149static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
@@ -130,6 +152,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
130 .reg = &btmrvl_reg_8688, 152 .reg = &btmrvl_reg_8688,
131 .support_pscan_win_report = false, 153 .support_pscan_win_report = false,
132 .sd_blksz_fw_dl = 64, 154 .sd_blksz_fw_dl = 64,
155 .supports_fw_dump = false,
133}; 156};
134 157
135static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { 158static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
@@ -138,6 +161,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
138 .reg = &btmrvl_reg_87xx, 161 .reg = &btmrvl_reg_87xx,
139 .support_pscan_win_report = false, 162 .support_pscan_win_report = false,
140 .sd_blksz_fw_dl = 256, 163 .sd_blksz_fw_dl = 256,
164 .supports_fw_dump = false,
141}; 165};
142 166
143static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = { 167static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
@@ -146,6 +170,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
146 .reg = &btmrvl_reg_87xx, 170 .reg = &btmrvl_reg_87xx,
147 .support_pscan_win_report = false, 171 .support_pscan_win_report = false,
148 .sd_blksz_fw_dl = 256, 172 .sd_blksz_fw_dl = 256,
173 .supports_fw_dump = false,
149}; 174};
150 175
151static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = { 176static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = {
@@ -154,6 +179,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = {
154 .reg = &btmrvl_reg_8887, 179 .reg = &btmrvl_reg_8887,
155 .support_pscan_win_report = true, 180 .support_pscan_win_report = true,
156 .sd_blksz_fw_dl = 256, 181 .sd_blksz_fw_dl = 256,
182 .supports_fw_dump = false,
157}; 183};
158 184
159static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = { 185static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
@@ -162,6 +188,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
162 .reg = &btmrvl_reg_8897, 188 .reg = &btmrvl_reg_8897,
163 .support_pscan_win_report = true, 189 .support_pscan_win_report = true,
164 .sd_blksz_fw_dl = 256, 190 .sd_blksz_fw_dl = 256,
191 .supports_fw_dump = true,
165}; 192};
166 193
167static const struct sdio_device_id btmrvl_sdio_ids[] = { 194static const struct sdio_device_id btmrvl_sdio_ids[] = {
@@ -1080,6 +1107,277 @@ static int btmrvl_sdio_wakeup_fw(struct btmrvl_private *priv)
1080 return ret; 1107 return ret;
1081} 1108}
1082 1109
1110static void btmrvl_sdio_dump_regs(struct btmrvl_private *priv)
1111{
1112 struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
1113 int ret = 0;
1114 unsigned int reg, reg_start, reg_end;
1115 char buf[256], *ptr;
1116 u8 loop, func, data;
1117 int MAX_LOOP = 2;
1118
1119 btmrvl_sdio_wakeup_fw(priv);
1120 sdio_claim_host(card->func);
1121
1122 for (loop = 0; loop < MAX_LOOP; loop++) {
1123 memset(buf, 0, sizeof(buf));
1124 ptr = buf;
1125
1126 if (loop == 0) {
1127 /* Read the registers of SDIO function0 */
1128 func = loop;
1129 reg_start = 0;
1130 reg_end = 9;
1131 } else {
1132 func = 2;
1133 reg_start = 0;
1134 reg_end = 0x09;
1135 }
1136
1137 ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ",
1138 func, reg_start, reg_end);
1139 for (reg = reg_start; reg <= reg_end; reg++) {
1140 if (func == 0)
1141 data = sdio_f0_readb(card->func, reg, &ret);
1142 else
1143 data = sdio_readb(card->func, reg, &ret);
1144
1145 if (!ret) {
1146 ptr += sprintf(ptr, "%02x ", data);
1147 } else {
1148 ptr += sprintf(ptr, "ERR");
1149 break;
1150 }
1151 }
1152
1153 BT_INFO("%s", buf);
1154 }
1155
1156 sdio_release_host(card->func);
1157}
1158
1159/* This function read/write firmware */
1160static enum
1161rdwr_status btmrvl_sdio_rdwr_firmware(struct btmrvl_private *priv,
1162 u8 doneflag)
1163{
1164 struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
1165 int ret, tries;
1166 u8 ctrl_data = 0;
1167
1168 sdio_writeb(card->func, FW_DUMP_HOST_READY, card->reg->fw_dump_ctrl,
1169 &ret);
1170
1171 if (ret) {
1172 BT_ERR("SDIO write err");
1173 return RDWR_STATUS_FAILURE;
1174 }
1175
1176 for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
1177 ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl,
1178 &ret);
1179
1180 if (ret) {
1181 BT_ERR("SDIO read err");
1182 return RDWR_STATUS_FAILURE;
1183 }
1184
1185 if (ctrl_data == FW_DUMP_DONE)
1186 break;
1187 if (doneflag && ctrl_data == doneflag)
1188 return RDWR_STATUS_DONE;
1189 if (ctrl_data != FW_DUMP_HOST_READY) {
1190 BT_INFO("The ctrl reg was changed, re-try again!");
1191 sdio_writeb(card->func, FW_DUMP_HOST_READY,
1192 card->reg->fw_dump_ctrl, &ret);
1193 if (ret) {
1194 BT_ERR("SDIO write err");
1195 return RDWR_STATUS_FAILURE;
1196 }
1197 }
1198 usleep_range(100, 200);
1199 }
1200
1201 if (ctrl_data == FW_DUMP_HOST_READY) {
1202 BT_ERR("Fail to pull ctrl_data");
1203 return RDWR_STATUS_FAILURE;
1204 }
1205
1206 return RDWR_STATUS_SUCCESS;
1207}
1208
1209/* This function dump sdio register and memory data */
1210static void btmrvl_sdio_dump_firmware(struct btmrvl_private *priv)
1211{
1212 struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
1213 int ret = 0;
1214 unsigned int reg, reg_start, reg_end;
1215 enum rdwr_status stat;
1216 u8 *dbg_ptr, *end_ptr, *fw_dump_data, *fw_dump_ptr;
1217 u8 dump_num, idx, i, read_reg, doneflag = 0;
1218 u32 memory_size, fw_dump_len = 0;
1219
1220 /* dump sdio register first */
1221 btmrvl_sdio_dump_regs(priv);
1222
1223 if (!card->supports_fw_dump) {
1224 BT_ERR("Firmware dump not supported for this card!");
1225 return;
1226 }
1227
1228 for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) {
1229 struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
1230
1231 if (entry->mem_ptr) {
1232 vfree(entry->mem_ptr);
1233 entry->mem_ptr = NULL;
1234 }
1235 entry->mem_size = 0;
1236 }
1237
1238 btmrvl_sdio_wakeup_fw(priv);
1239 sdio_claim_host(card->func);
1240
1241 BT_INFO("== btmrvl firmware dump start ==");
1242
1243 stat = btmrvl_sdio_rdwr_firmware(priv, doneflag);
1244 if (stat == RDWR_STATUS_FAILURE)
1245 goto done;
1246
1247 reg = card->reg->fw_dump_start;
1248 /* Read the number of the memories which will dump */
1249 dump_num = sdio_readb(card->func, reg, &ret);
1250
1251 if (ret) {
1252 BT_ERR("SDIO read memory length err");
1253 goto done;
1254 }
1255
1256 /* Read the length of every memory which will dump */
1257 for (idx = 0; idx < dump_num; idx++) {
1258 struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
1259
1260 stat = btmrvl_sdio_rdwr_firmware(priv, doneflag);
1261 if (stat == RDWR_STATUS_FAILURE)
1262 goto done;
1263
1264 memory_size = 0;
1265 reg = card->reg->fw_dump_start;
1266 for (i = 0; i < 4; i++) {
1267 read_reg = sdio_readb(card->func, reg, &ret);
1268 if (ret) {
1269 BT_ERR("SDIO read err");
1270 goto done;
1271 }
1272 memory_size |= (read_reg << i*8);
1273 reg++;
1274 }
1275
1276 if (memory_size == 0) {
1277 BT_INFO("Firmware dump finished!");
1278 break;
1279 }
1280
1281 BT_INFO("%s_SIZE=0x%x", entry->mem_name, memory_size);
1282 entry->mem_ptr = vzalloc(memory_size + 1);
1283 entry->mem_size = memory_size;
1284 if (!entry->mem_ptr) {
1285 BT_ERR("Vzalloc %s failed", entry->mem_name);
1286 goto done;
1287 }
1288
1289 fw_dump_len += (strlen("========Start dump ") +
1290 strlen(entry->mem_name) +
1291 strlen("========\n") +
1292 (memory_size + 1) +
1293 strlen("\n========End dump========\n"));
1294
1295 dbg_ptr = entry->mem_ptr;
1296 end_ptr = dbg_ptr + memory_size;
1297
1298 doneflag = entry->done_flag;
1299 BT_INFO("Start %s output, please wait...",
1300 entry->mem_name);
1301
1302 do {
1303 stat = btmrvl_sdio_rdwr_firmware(priv, doneflag);
1304 if (stat == RDWR_STATUS_FAILURE)
1305 goto done;
1306
1307 reg_start = card->reg->fw_dump_start;
1308 reg_end = card->reg->fw_dump_end;
1309 for (reg = reg_start; reg <= reg_end; reg++) {
1310 *dbg_ptr = sdio_readb(card->func, reg, &ret);
1311 if (ret) {
1312 BT_ERR("SDIO read err");
1313 goto done;
1314 }
1315 if (dbg_ptr < end_ptr)
1316 dbg_ptr++;
1317 else
1318 BT_ERR("Allocated buffer not enough");
1319 }
1320
1321 if (stat != RDWR_STATUS_DONE) {
1322 continue;
1323 } else {
1324 BT_INFO("%s done: size=0x%tx",
1325 entry->mem_name,
1326 dbg_ptr - entry->mem_ptr);
1327 break;
1328 }
1329 } while (1);
1330 }
1331
1332 BT_INFO("== btmrvl firmware dump end ==");
1333
1334done:
1335 sdio_release_host(card->func);
1336
1337 if (fw_dump_len == 0)
1338 return;
1339
1340 fw_dump_data = vzalloc(fw_dump_len+1);
1341 if (!fw_dump_data) {
1342 BT_ERR("Vzalloc fw_dump_data fail!");
1343 return;
1344 }
1345 fw_dump_ptr = fw_dump_data;
1346
1347 /* Dump all the memory data into single file, a userspace script will
1348 be used to split all the memory data to multiple files*/
1349 BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump start");
1350 for (idx = 0; idx < dump_num; idx++) {
1351 struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
1352
1353 if (entry->mem_ptr) {
1354 strcpy(fw_dump_ptr, "========Start dump ");
1355 fw_dump_ptr += strlen("========Start dump ");
1356
1357 strcpy(fw_dump_ptr, entry->mem_name);
1358 fw_dump_ptr += strlen(entry->mem_name);
1359
1360 strcpy(fw_dump_ptr, "========\n");
1361 fw_dump_ptr += strlen("========\n");
1362
1363 memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size);
1364 fw_dump_ptr += entry->mem_size;
1365
1366 strcpy(fw_dump_ptr, "\n========End dump========\n");
1367 fw_dump_ptr += strlen("\n========End dump========\n");
1368
1369 vfree(mem_type_mapping_tbl[idx].mem_ptr);
1370 mem_type_mapping_tbl[idx].mem_ptr = NULL;
1371 }
1372 }
1373
1374 /* fw_dump_data will be free in device coredump release function
1375 after 5 min*/
1376 dev_coredumpv(&priv->btmrvl_dev.hcidev->dev, fw_dump_data,
1377 fw_dump_len, GFP_KERNEL);
1378 BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump end");
1379}
1380
1083static int btmrvl_sdio_probe(struct sdio_func *func, 1381static int btmrvl_sdio_probe(struct sdio_func *func,
1084 const struct sdio_device_id *id) 1382 const struct sdio_device_id *id)
1085{ 1383{
@@ -1103,6 +1401,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
1103 card->reg = data->reg; 1401 card->reg = data->reg;
1104 card->sd_blksz_fw_dl = data->sd_blksz_fw_dl; 1402 card->sd_blksz_fw_dl = data->sd_blksz_fw_dl;
1105 card->support_pscan_win_report = data->support_pscan_win_report; 1403 card->support_pscan_win_report = data->support_pscan_win_report;
1404 card->supports_fw_dump = data->supports_fw_dump;
1106 } 1405 }
1107 1406
1108 if (btmrvl_sdio_register_dev(card) < 0) { 1407 if (btmrvl_sdio_register_dev(card) < 0) {
@@ -1134,6 +1433,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
1134 priv->hw_host_to_card = btmrvl_sdio_host_to_card; 1433 priv->hw_host_to_card = btmrvl_sdio_host_to_card;
1135 priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw; 1434 priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw;
1136 priv->hw_process_int_status = btmrvl_sdio_process_int_status; 1435 priv->hw_process_int_status = btmrvl_sdio_process_int_status;
1436 priv->firmware_dump = btmrvl_sdio_dump_firmware;
1137 1437
1138 if (btmrvl_register_hdev(priv)) { 1438 if (btmrvl_register_hdev(priv)) {
1139 BT_ERR("Register hdev failed!"); 1439 BT_ERR("Register hdev failed!");
diff --git a/drivers/bluetooth/btmrvl_sdio.h b/drivers/bluetooth/btmrvl_sdio.h
index 453559f98a75..1a3bd064c442 100644
--- a/drivers/bluetooth/btmrvl_sdio.h
+++ b/drivers/bluetooth/btmrvl_sdio.h
@@ -81,6 +81,9 @@ struct btmrvl_sdio_card_reg {
81 bool int_read_to_clear; 81 bool int_read_to_clear;
82 u8 host_int_rsr; 82 u8 host_int_rsr;
83 u8 card_misc_cfg; 83 u8 card_misc_cfg;
84 u8 fw_dump_ctrl;
85 u8 fw_dump_start;
86 u8 fw_dump_end;
84}; 87};
85 88
86struct btmrvl_sdio_card { 89struct btmrvl_sdio_card {
@@ -90,6 +93,7 @@ struct btmrvl_sdio_card {
90 const char *firmware; 93 const char *firmware;
91 const struct btmrvl_sdio_card_reg *reg; 94 const struct btmrvl_sdio_card_reg *reg;
92 bool support_pscan_win_report; 95 bool support_pscan_win_report;
96 bool supports_fw_dump;
93 u16 sd_blksz_fw_dl; 97 u16 sd_blksz_fw_dl;
94 u8 rx_unit; 98 u8 rx_unit;
95 struct btmrvl_private *priv; 99 struct btmrvl_private *priv;
@@ -101,6 +105,7 @@ struct btmrvl_sdio_device {
101 const struct btmrvl_sdio_card_reg *reg; 105 const struct btmrvl_sdio_card_reg *reg;
102 const bool support_pscan_win_report; 106 const bool support_pscan_win_report;
103 u16 sd_blksz_fw_dl; 107 u16 sd_blksz_fw_dl;
108 bool supports_fw_dump;
104}; 109};
105 110
106 111