Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Xinming Hu and committed by
Marcel Holtmann
dc759613 7365d475

+364
+1
drivers/bluetooth/Kconfig
··· 210 210 tristate "Marvell BT-over-SDIO driver" 211 211 depends on BT_MRVL && MMC 212 212 select FW_LOADER 213 + select WANT_DEV_COREDUMP 213 214 help 214 215 The driver for Marvell Bluetooth chipsets with SDIO interface. 215 216
+31
drivers/bluetooth/btmrvl_debugfs.c
··· 167 167 .llseek = default_llseek, 168 168 }; 169 169 170 + static 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 + 193 + static const struct file_operations btmrvl_fwdump_fops = { 194 + .write = btmrvl_fwdump_write, 195 + .open = simple_open, 196 + .llseek = default_llseek, 197 + }; 198 + 170 199 void btmrvl_debugfs_init(struct hci_dev *hdev) 171 200 { 172 201 struct btmrvl_private *priv = hci_get_drvdata(hdev); ··· 226 197 priv, &btmrvl_hscmd_fops); 227 198 debugfs_create_file("hscfgcmd", 0644, dbg->config_dir, 228 199 priv, &btmrvl_hscfgcmd_fops); 200 + debugfs_create_file("fw_dump", 0200, dbg->config_dir, 201 + priv, &btmrvl_fwdump_fops); 229 202 230 203 dbg->status_dir = debugfs_create_dir("status", hdev->debugfs); 231 204 debugfs_create_u8("curpsmode", 0444, dbg->status_dir,
+20
drivers/bluetooth/btmrvl_drv.h
··· 32 32 /* Time to wait for command response in millisecond */ 33 33 #define WAIT_UNTIL_CMD_RESP 5000 34 34 35 + enum 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 + 46 + struct 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 + 35 53 struct btmrvl_thread { 36 54 struct task_struct *task; 37 55 wait_queue_head_t wait_q; ··· 99 81 u8 *payload, u16 nb); 100 82 int (*hw_wakeup_firmware) (struct btmrvl_private *priv); 101 83 int (*hw_process_int_status) (struct btmrvl_private *priv); 84 + void (*firmware_dump)(struct btmrvl_private *priv); 102 85 spinlock_t driver_lock; /* spinlock used by driver */ 103 86 #ifdef CONFIG_DEBUG_FS 104 87 void *debugfs_data; ··· 170 151 int btmrvl_enable_ps(struct btmrvl_private *priv); 171 152 int btmrvl_prepare_command(struct btmrvl_private *priv); 172 153 int btmrvl_enable_hs(struct btmrvl_private *priv); 154 + void btmrvl_firmware_dump(struct btmrvl_private *priv); 173 155 174 156 #ifdef CONFIG_DEBUG_FS 175 157 void btmrvl_debugfs_init(struct hci_dev *hdev);
+7
drivers/bluetooth/btmrvl_main.c
··· 22 22 #include <linux/of.h> 23 23 #include <net/bluetooth/bluetooth.h> 24 24 #include <net/bluetooth/hci_core.h> 25 + #include <linux/mmc/sdio_func.h> 25 26 26 27 #include "btmrvl_drv.h" 27 28 #include "btmrvl_sdio.h" ··· 334 333 } 335 334 336 335 return ret; 336 + } 337 + 338 + void btmrvl_firmware_dump(struct btmrvl_private *priv) 339 + { 340 + if (priv->firmware_dump) 341 + priv->firmware_dump(priv); 337 342 } 338 343 339 344 static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
+300
drivers/bluetooth/btmrvl_sdio.c
··· 24 24 #include <linux/mmc/sdio_ids.h> 25 25 #include <linux/mmc/sdio_func.h> 26 26 #include <linux/module.h> 27 + #include <linux/devcoredump.h> 27 28 28 29 #include <net/bluetooth/bluetooth.h> 29 30 #include <net/bluetooth/hci_core.h> ··· 33 32 #include "btmrvl_sdio.h" 34 33 35 34 #define VERSION "1.0" 35 + 36 + static struct memory_type_mapping mem_type_mapping_tbl[] = { 37 + {"ITCM", NULL, 0, 0xF0}, 38 + {"DTCM", NULL, 0, 0xF1}, 39 + {"SQRAM", NULL, 0, 0xF2}, 40 + {"APU", NULL, 0, 0xF3}, 41 + {"CIU", NULL, 0, 0xF4}, 42 + {"ICU", NULL, 0, 0xF5}, 43 + {"MAC", NULL, 0, 0xF6}, 44 + {"EXT7", NULL, 0, 0xF7}, 45 + {"EXT8", NULL, 0, 0xF8}, 46 + {"EXT9", NULL, 0, 0xF9}, 47 + {"EXT10", NULL, 0, 0xFA}, 48 + {"EXT11", NULL, 0, 0xFB}, 49 + {"EXT12", NULL, 0, 0xFC}, 50 + {"EXT13", NULL, 0, 0xFD}, 51 + {"EXTLAST", NULL, 0, 0xFE}, 52 + }; 36 53 37 54 /* The btmrvl_sdio_remove() callback function is called 38 55 * when user removes this module from kernel space or ejects ··· 141 122 .int_read_to_clear = true, 142 123 .host_int_rsr = 0x01, 143 124 .card_misc_cfg = 0xcc, 125 + .fw_dump_ctrl = 0xe2, 126 + .fw_dump_start = 0xe3, 127 + .fw_dump_end = 0xea, 144 128 }; 145 129 146 130 static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { ··· 152 130 .reg = &btmrvl_reg_8688, 153 131 .support_pscan_win_report = false, 154 132 .sd_blksz_fw_dl = 64, 133 + .supports_fw_dump = false, 155 134 }; 156 135 157 136 static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { ··· 161 138 .reg = &btmrvl_reg_87xx, 162 139 .support_pscan_win_report = false, 163 140 .sd_blksz_fw_dl = 256, 141 + .supports_fw_dump = false, 164 142 }; 165 143 166 144 static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = { ··· 170 146 .reg = &btmrvl_reg_87xx, 171 147 .support_pscan_win_report = false, 172 148 .sd_blksz_fw_dl = 256, 149 + .supports_fw_dump = false, 173 150 }; 174 151 175 152 static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = { ··· 179 154 .reg = &btmrvl_reg_8887, 180 155 .support_pscan_win_report = true, 181 156 .sd_blksz_fw_dl = 256, 157 + .supports_fw_dump = false, 182 158 }; 183 159 184 160 static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = { ··· 188 162 .reg = &btmrvl_reg_8897, 189 163 .support_pscan_win_report = true, 190 164 .sd_blksz_fw_dl = 256, 165 + .supports_fw_dump = true, 191 166 }; 192 167 193 168 static const struct sdio_device_id btmrvl_sdio_ids[] = { ··· 1107 1080 return ret; 1108 1081 } 1109 1082 1083 + static void btmrvl_sdio_dump_regs(struct btmrvl_private *priv) 1084 + { 1085 + struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; 1086 + int ret = 0; 1087 + unsigned int reg, reg_start, reg_end; 1088 + char buf[256], *ptr; 1089 + u8 loop, func, data; 1090 + int MAX_LOOP = 2; 1091 + 1092 + btmrvl_sdio_wakeup_fw(priv); 1093 + sdio_claim_host(card->func); 1094 + 1095 + for (loop = 0; loop < MAX_LOOP; loop++) { 1096 + memset(buf, 0, sizeof(buf)); 1097 + ptr = buf; 1098 + 1099 + if (loop == 0) { 1100 + /* Read the registers of SDIO function0 */ 1101 + func = loop; 1102 + reg_start = 0; 1103 + reg_end = 9; 1104 + } else { 1105 + func = 2; 1106 + reg_start = 0; 1107 + reg_end = 0x09; 1108 + } 1109 + 1110 + ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", 1111 + func, reg_start, reg_end); 1112 + for (reg = reg_start; reg <= reg_end; reg++) { 1113 + if (func == 0) 1114 + data = sdio_f0_readb(card->func, reg, &ret); 1115 + else 1116 + data = sdio_readb(card->func, reg, &ret); 1117 + 1118 + if (!ret) { 1119 + ptr += sprintf(ptr, "%02x ", data); 1120 + } else { 1121 + ptr += sprintf(ptr, "ERR"); 1122 + break; 1123 + } 1124 + } 1125 + 1126 + BT_INFO("%s", buf); 1127 + } 1128 + 1129 + sdio_release_host(card->func); 1130 + } 1131 + 1132 + /* This function read/write firmware */ 1133 + static enum 1134 + rdwr_status btmrvl_sdio_rdwr_firmware(struct btmrvl_private *priv, 1135 + u8 doneflag) 1136 + { 1137 + struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; 1138 + int ret, tries; 1139 + u8 ctrl_data = 0; 1140 + 1141 + sdio_writeb(card->func, FW_DUMP_HOST_READY, card->reg->fw_dump_ctrl, 1142 + &ret); 1143 + 1144 + if (ret) { 1145 + BT_ERR("SDIO write err"); 1146 + return RDWR_STATUS_FAILURE; 1147 + } 1148 + 1149 + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { 1150 + ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl, 1151 + &ret); 1152 + 1153 + if (ret) { 1154 + BT_ERR("SDIO read err"); 1155 + return RDWR_STATUS_FAILURE; 1156 + } 1157 + 1158 + if (ctrl_data == FW_DUMP_DONE) 1159 + break; 1160 + if (doneflag && ctrl_data == doneflag) 1161 + return RDWR_STATUS_DONE; 1162 + if (ctrl_data != FW_DUMP_HOST_READY) { 1163 + BT_INFO("The ctrl reg was changed, re-try again!"); 1164 + sdio_writeb(card->func, FW_DUMP_HOST_READY, 1165 + card->reg->fw_dump_ctrl, &ret); 1166 + if (ret) { 1167 + BT_ERR("SDIO write err"); 1168 + return RDWR_STATUS_FAILURE; 1169 + } 1170 + } 1171 + usleep_range(100, 200); 1172 + } 1173 + 1174 + if (ctrl_data == FW_DUMP_HOST_READY) { 1175 + BT_ERR("Fail to pull ctrl_data"); 1176 + return RDWR_STATUS_FAILURE; 1177 + } 1178 + 1179 + return RDWR_STATUS_SUCCESS; 1180 + } 1181 + 1182 + /* This function dump sdio register and memory data */ 1183 + static void btmrvl_sdio_dump_firmware(struct btmrvl_private *priv) 1184 + { 1185 + struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; 1186 + int ret = 0; 1187 + unsigned int reg, reg_start, reg_end; 1188 + enum rdwr_status stat; 1189 + u8 *dbg_ptr, *end_ptr, *fw_dump_data, *fw_dump_ptr; 1190 + u8 dump_num, idx, i, read_reg, doneflag = 0; 1191 + u32 memory_size, fw_dump_len = 0; 1192 + 1193 + /* dump sdio register first */ 1194 + btmrvl_sdio_dump_regs(priv); 1195 + 1196 + if (!card->supports_fw_dump) { 1197 + BT_ERR("Firmware dump not supported for this card!"); 1198 + return; 1199 + } 1200 + 1201 + for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { 1202 + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; 1203 + 1204 + if (entry->mem_ptr) { 1205 + vfree(entry->mem_ptr); 1206 + entry->mem_ptr = NULL; 1207 + } 1208 + entry->mem_size = 0; 1209 + } 1210 + 1211 + btmrvl_sdio_wakeup_fw(priv); 1212 + sdio_claim_host(card->func); 1213 + 1214 + BT_INFO("== btmrvl firmware dump start =="); 1215 + 1216 + stat = btmrvl_sdio_rdwr_firmware(priv, doneflag); 1217 + if (stat == RDWR_STATUS_FAILURE) 1218 + goto done; 1219 + 1220 + reg = card->reg->fw_dump_start; 1221 + /* Read the number of the memories which will dump */ 1222 + dump_num = sdio_readb(card->func, reg, &ret); 1223 + 1224 + if (ret) { 1225 + BT_ERR("SDIO read memory length err"); 1226 + goto done; 1227 + } 1228 + 1229 + /* Read the length of every memory which will dump */ 1230 + for (idx = 0; idx < dump_num; idx++) { 1231 + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; 1232 + 1233 + stat = btmrvl_sdio_rdwr_firmware(priv, doneflag); 1234 + if (stat == RDWR_STATUS_FAILURE) 1235 + goto done; 1236 + 1237 + memory_size = 0; 1238 + reg = card->reg->fw_dump_start; 1239 + for (i = 0; i < 4; i++) { 1240 + read_reg = sdio_readb(card->func, reg, &ret); 1241 + if (ret) { 1242 + BT_ERR("SDIO read err"); 1243 + goto done; 1244 + } 1245 + memory_size |= (read_reg << i*8); 1246 + reg++; 1247 + } 1248 + 1249 + if (memory_size == 0) { 1250 + BT_INFO("Firmware dump finished!"); 1251 + break; 1252 + } 1253 + 1254 + BT_INFO("%s_SIZE=0x%x", entry->mem_name, memory_size); 1255 + entry->mem_ptr = vzalloc(memory_size + 1); 1256 + entry->mem_size = memory_size; 1257 + if (!entry->mem_ptr) { 1258 + BT_ERR("Vzalloc %s failed", entry->mem_name); 1259 + goto done; 1260 + } 1261 + 1262 + fw_dump_len += (strlen("========Start dump ") + 1263 + strlen(entry->mem_name) + 1264 + strlen("========\n") + 1265 + (memory_size + 1) + 1266 + strlen("\n========End dump========\n")); 1267 + 1268 + dbg_ptr = entry->mem_ptr; 1269 + end_ptr = dbg_ptr + memory_size; 1270 + 1271 + doneflag = entry->done_flag; 1272 + BT_INFO("Start %s output, please wait...", 1273 + entry->mem_name); 1274 + 1275 + do { 1276 + stat = btmrvl_sdio_rdwr_firmware(priv, doneflag); 1277 + if (stat == RDWR_STATUS_FAILURE) 1278 + goto done; 1279 + 1280 + reg_start = card->reg->fw_dump_start; 1281 + reg_end = card->reg->fw_dump_end; 1282 + for (reg = reg_start; reg <= reg_end; reg++) { 1283 + *dbg_ptr = sdio_readb(card->func, reg, &ret); 1284 + if (ret) { 1285 + BT_ERR("SDIO read err"); 1286 + goto done; 1287 + } 1288 + if (dbg_ptr < end_ptr) 1289 + dbg_ptr++; 1290 + else 1291 + BT_ERR("Allocated buffer not enough"); 1292 + } 1293 + 1294 + if (stat != RDWR_STATUS_DONE) { 1295 + continue; 1296 + } else { 1297 + BT_INFO("%s done: size=0x%tx", 1298 + entry->mem_name, 1299 + dbg_ptr - entry->mem_ptr); 1300 + break; 1301 + } 1302 + } while (1); 1303 + } 1304 + 1305 + BT_INFO("== btmrvl firmware dump end =="); 1306 + 1307 + done: 1308 + sdio_release_host(card->func); 1309 + 1310 + if (fw_dump_len == 0) 1311 + return; 1312 + 1313 + fw_dump_data = vzalloc(fw_dump_len+1); 1314 + if (!fw_dump_data) { 1315 + BT_ERR("Vzalloc fw_dump_data fail!"); 1316 + return; 1317 + } 1318 + fw_dump_ptr = fw_dump_data; 1319 + 1320 + /* Dump all the memory data into single file, a userspace script will 1321 + be used to split all the memory data to multiple files*/ 1322 + BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump start"); 1323 + for (idx = 0; idx < dump_num; idx++) { 1324 + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; 1325 + 1326 + if (entry->mem_ptr) { 1327 + strcpy(fw_dump_ptr, "========Start dump "); 1328 + fw_dump_ptr += strlen("========Start dump "); 1329 + 1330 + strcpy(fw_dump_ptr, entry->mem_name); 1331 + fw_dump_ptr += strlen(entry->mem_name); 1332 + 1333 + strcpy(fw_dump_ptr, "========\n"); 1334 + fw_dump_ptr += strlen("========\n"); 1335 + 1336 + memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size); 1337 + fw_dump_ptr += entry->mem_size; 1338 + 1339 + strcpy(fw_dump_ptr, "\n========End dump========\n"); 1340 + fw_dump_ptr += strlen("\n========End dump========\n"); 1341 + 1342 + vfree(mem_type_mapping_tbl[idx].mem_ptr); 1343 + mem_type_mapping_tbl[idx].mem_ptr = NULL; 1344 + } 1345 + } 1346 + 1347 + /* fw_dump_data will be free in device coredump release function 1348 + after 5 min*/ 1349 + dev_coredumpv(&priv->btmrvl_dev.hcidev->dev, fw_dump_data, 1350 + fw_dump_len, GFP_KERNEL); 1351 + BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump end"); 1352 + } 1353 + 1110 1354 static int btmrvl_sdio_probe(struct sdio_func *func, 1111 1355 const struct sdio_device_id *id) 1112 1356 { ··· 1401 1103 card->reg = data->reg; 1402 1104 card->sd_blksz_fw_dl = data->sd_blksz_fw_dl; 1403 1105 card->support_pscan_win_report = data->support_pscan_win_report; 1106 + card->supports_fw_dump = data->supports_fw_dump; 1404 1107 } 1405 1108 1406 1109 if (btmrvl_sdio_register_dev(card) < 0) { ··· 1433 1134 priv->hw_host_to_card = btmrvl_sdio_host_to_card; 1434 1135 priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw; 1435 1136 priv->hw_process_int_status = btmrvl_sdio_process_int_status; 1137 + priv->firmware_dump = btmrvl_sdio_dump_firmware; 1436 1138 1437 1139 if (btmrvl_register_hdev(priv)) { 1438 1140 BT_ERR("Register hdev failed!");
+5
drivers/bluetooth/btmrvl_sdio.h
··· 81 81 bool int_read_to_clear; 82 82 u8 host_int_rsr; 83 83 u8 card_misc_cfg; 84 + u8 fw_dump_ctrl; 85 + u8 fw_dump_start; 86 + u8 fw_dump_end; 84 87 }; 85 88 86 89 struct btmrvl_sdio_card { ··· 93 90 const char *firmware; 94 91 const struct btmrvl_sdio_card_reg *reg; 95 92 bool support_pscan_win_report; 93 + bool supports_fw_dump; 96 94 u16 sd_blksz_fw_dl; 97 95 u8 rx_unit; 98 96 struct btmrvl_private *priv; ··· 105 101 const struct btmrvl_sdio_card_reg *reg; 106 102 const bool support_pscan_win_report; 107 103 u16 sd_blksz_fw_dl; 104 + bool supports_fw_dump; 108 105 }; 109 106 110 107