diff options
author | Xinming Hu <huxm@marvell.com> | 2014-11-24 05:40:53 -0500 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2014-12-03 11:35:51 -0500 |
commit | dc759613b0247eb1658d3992f50ba3fad5b61d31 (patch) | |
tree | 5ae91a8d93f48204c9c3658b2eba00594c2fb689 /drivers/bluetooth | |
parent | 7365d475bf6a0e2497ac4ef29474fffb91d024f1 (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/Kconfig | 1 | ||||
-rw-r--r-- | drivers/bluetooth/btmrvl_debugfs.c | 31 | ||||
-rw-r--r-- | drivers/bluetooth/btmrvl_drv.h | 20 | ||||
-rw-r--r-- | drivers/bluetooth/btmrvl_main.c | 7 | ||||
-rw-r--r-- | drivers/bluetooth/btmrvl_sdio.c | 300 | ||||
-rw-r--r-- | drivers/bluetooth/btmrvl_sdio.h | 5 |
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 | ||
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 | void btmrvl_debugfs_init(struct hci_dev *hdev) | 199 | void 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 | ||
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 | struct btmrvl_thread { | 53 | struct 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); | |||
151 | int btmrvl_enable_ps(struct btmrvl_private *priv); | 170 | int btmrvl_enable_ps(struct btmrvl_private *priv); |
152 | int btmrvl_prepare_command(struct btmrvl_private *priv); | 171 | int btmrvl_prepare_command(struct btmrvl_private *priv); |
153 | int btmrvl_enable_hs(struct btmrvl_private *priv); | 172 | int btmrvl_enable_hs(struct btmrvl_private *priv); |
173 | void btmrvl_firmware_dump(struct btmrvl_private *priv); | ||
154 | 174 | ||
155 | #ifdef CONFIG_DEBUG_FS | 175 | #ifdef CONFIG_DEBUG_FS |
156 | void btmrvl_debugfs_init(struct hci_dev *hdev); | 176 | void 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 | ||
339 | void btmrvl_firmware_dump(struct btmrvl_private *priv) | ||
340 | { | ||
341 | if (priv->firmware_dump) | ||
342 | priv->firmware_dump(priv); | ||
343 | } | ||
344 | |||
338 | static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb) | 345 | static 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 | ||
37 | static 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 | ||
127 | static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { | 149 | static 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 | ||
135 | static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { | 158 | static 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 | ||
143 | static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = { | 167 | static 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 | ||
151 | static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = { | 176 | static 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 | ||
159 | static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = { | 185 | static 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 | ||
167 | static const struct sdio_device_id btmrvl_sdio_ids[] = { | 194 | static 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 | ||
1110 | static 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 */ | ||
1160 | static enum | ||
1161 | rdwr_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 */ | ||
1210 | static 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 | |||
1334 | done: | ||
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 | |||
1083 | static int btmrvl_sdio_probe(struct sdio_func *func, | 1381 | static 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 | ||
86 | struct btmrvl_sdio_card { | 89 | struct 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 | ||