diff options
author | Anirban Chakraborty <anirban.chakraborty@qlogic.com> | 2011-05-12 08:48:33 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-05-13 14:44:12 -0400 |
commit | 18f2f616be88736f5daf31d9d40e027abbd607ed (patch) | |
tree | c4ef622c5f4c187099cfac33f2fc6313c4efe790 | |
parent | 1dbf53a28262aa89ecbe653e8a9127c0baef9bc4 (diff) |
qlcnic: FW dump support
Added code to take FW dump.
o Driver queries FW at the init time and gets the dump template
o It takes FW dump as per the dump template
o Level of FW dump (and its size) is configured via dump flag
Signed-off-by: Sritej Velaga <sritej.velaga@qlogic.com>
Signed-off-by: Anirban Chakraborty <anirban.chakraborty@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/qlcnic/qlcnic.h | 176 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_ctx.c | 91 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_hdr.h | 40 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_hw.c | 459 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_init.c | 4 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_main.c | 13 |
6 files changed, 774 insertions, 9 deletions
diff --git a/drivers/net/qlcnic/qlcnic.h b/drivers/net/qlcnic/qlcnic.h index f729363b3fc3..689adea62700 100644 --- a/drivers/net/qlcnic/qlcnic.h +++ b/drivers/net/qlcnic/qlcnic.h | |||
@@ -411,6 +411,29 @@ struct qlcnic_nic_intr_coalesce { | |||
411 | u32 timer_out; | 411 | u32 timer_out; |
412 | }; | 412 | }; |
413 | 413 | ||
414 | struct qlcnic_dump_template_hdr { | ||
415 | __le32 type; | ||
416 | __le32 offset; | ||
417 | __le32 size; | ||
418 | __le32 cap_mask; | ||
419 | __le32 num_entries; | ||
420 | __le32 version; | ||
421 | __le32 timestamp; | ||
422 | __le32 checksum; | ||
423 | __le32 drv_cap_mask; | ||
424 | __le32 sys_info[3]; | ||
425 | __le32 saved_state[16]; | ||
426 | __le32 cap_sizes[8]; | ||
427 | __le32 rsvd[0]; | ||
428 | }; | ||
429 | |||
430 | struct qlcnic_fw_dump { | ||
431 | u8 clr; /* flag to indicate if dump is cleared */ | ||
432 | u32 size; /* total size of the dump */ | ||
433 | void *data; /* dump data area */ | ||
434 | struct qlcnic_dump_template_hdr *tmpl_hdr; | ||
435 | }; | ||
436 | |||
414 | /* | 437 | /* |
415 | * One hardware_context{} per adapter | 438 | * One hardware_context{} per adapter |
416 | * contains interrupt info as well shared hardware info. | 439 | * contains interrupt info as well shared hardware info. |
@@ -431,6 +454,7 @@ struct qlcnic_hardware_context { | |||
431 | u16 board_type; | 454 | u16 board_type; |
432 | 455 | ||
433 | struct qlcnic_nic_intr_coalesce coal; | 456 | struct qlcnic_nic_intr_coalesce coal; |
457 | struct qlcnic_fw_dump fw_dump; | ||
434 | }; | 458 | }; |
435 | 459 | ||
436 | struct qlcnic_adapter_stats { | 460 | struct qlcnic_adapter_stats { |
@@ -574,6 +598,8 @@ struct qlcnic_recv_context { | |||
574 | #define QLCNIC_CDRP_CMD_GET_ESWITCH_PORT_CONFIG 0x00000029 | 598 | #define QLCNIC_CDRP_CMD_GET_ESWITCH_PORT_CONFIG 0x00000029 |
575 | #define QLCNIC_CDRP_CMD_GET_ESWITCH_STATS 0x0000002a | 599 | #define QLCNIC_CDRP_CMD_GET_ESWITCH_STATS 0x0000002a |
576 | #define QLCNIC_CDRP_CMD_CONFIG_PORT 0x0000002E | 600 | #define QLCNIC_CDRP_CMD_CONFIG_PORT 0x0000002E |
601 | #define QLCNIC_CDRP_CMD_TEMP_SIZE 0x0000002f | ||
602 | #define QLCNIC_CDRP_CMD_GET_TEMP_HDR 0x00000030 | ||
577 | 603 | ||
578 | #define QLCNIC_RCODE_SUCCESS 0 | 604 | #define QLCNIC_RCODE_SUCCESS 0 |
579 | #define QLCNIC_RCODE_NOT_SUPPORTED 9 | 605 | #define QLCNIC_RCODE_NOT_SUPPORTED 9 |
@@ -1157,6 +1183,152 @@ struct qlcnic_esw_statistics { | |||
1157 | struct __qlcnic_esw_statistics tx; | 1183 | struct __qlcnic_esw_statistics tx; |
1158 | }; | 1184 | }; |
1159 | 1185 | ||
1186 | struct qlcnic_common_entry_hdr { | ||
1187 | __le32 type; | ||
1188 | __le32 offset; | ||
1189 | __le32 cap_size; | ||
1190 | u8 mask; | ||
1191 | u8 rsvd[2]; | ||
1192 | u8 flags; | ||
1193 | } __packed; | ||
1194 | |||
1195 | struct __crb { | ||
1196 | __le32 addr; | ||
1197 | u8 stride; | ||
1198 | u8 rsvd1[3]; | ||
1199 | __le32 data_size; | ||
1200 | __le32 no_ops; | ||
1201 | __le32 rsvd2[4]; | ||
1202 | } __packed; | ||
1203 | |||
1204 | struct __ctrl { | ||
1205 | __le32 addr; | ||
1206 | u8 stride; | ||
1207 | u8 index_a; | ||
1208 | __le16 timeout; | ||
1209 | __le32 data_size; | ||
1210 | __le32 no_ops; | ||
1211 | u8 opcode; | ||
1212 | u8 index_v; | ||
1213 | u8 shl_val; | ||
1214 | u8 shr_val; | ||
1215 | __le32 val1; | ||
1216 | __le32 val2; | ||
1217 | __le32 val3; | ||
1218 | } __packed; | ||
1219 | |||
1220 | struct __cache { | ||
1221 | __le32 addr; | ||
1222 | u8 stride; | ||
1223 | u8 rsvd; | ||
1224 | __le16 init_tag_val; | ||
1225 | __le32 size; | ||
1226 | __le32 no_ops; | ||
1227 | __le32 ctrl_addr; | ||
1228 | __le32 ctrl_val; | ||
1229 | __le32 read_addr; | ||
1230 | u8 read_addr_stride; | ||
1231 | u8 read_addr_num; | ||
1232 | u8 rsvd1[2]; | ||
1233 | } __packed; | ||
1234 | |||
1235 | struct __ocm { | ||
1236 | u8 rsvd[8]; | ||
1237 | __le32 size; | ||
1238 | __le32 no_ops; | ||
1239 | u8 rsvd1[8]; | ||
1240 | __le32 read_addr; | ||
1241 | __le32 read_addr_stride; | ||
1242 | } __packed; | ||
1243 | |||
1244 | struct __mem { | ||
1245 | u8 rsvd[24]; | ||
1246 | __le32 addr; | ||
1247 | __le32 size; | ||
1248 | } __packed; | ||
1249 | |||
1250 | struct __mux { | ||
1251 | __le32 addr; | ||
1252 | u8 rsvd[4]; | ||
1253 | __le32 size; | ||
1254 | __le32 no_ops; | ||
1255 | __le32 val; | ||
1256 | __le32 val_stride; | ||
1257 | __le32 read_addr; | ||
1258 | u8 rsvd2[4]; | ||
1259 | } __packed; | ||
1260 | |||
1261 | struct __queue { | ||
1262 | __le32 sel_addr; | ||
1263 | __le16 stride; | ||
1264 | u8 rsvd[2]; | ||
1265 | __le32 size; | ||
1266 | __le32 no_ops; | ||
1267 | u8 rsvd2[8]; | ||
1268 | __le32 read_addr; | ||
1269 | u8 read_addr_stride; | ||
1270 | u8 read_addr_cnt; | ||
1271 | u8 rsvd3[2]; | ||
1272 | } __packed; | ||
1273 | |||
1274 | struct qlcnic_dump_entry { | ||
1275 | struct qlcnic_common_entry_hdr hdr; | ||
1276 | union { | ||
1277 | struct __crb crb; | ||
1278 | struct __cache cache; | ||
1279 | struct __ocm ocm; | ||
1280 | struct __mem mem; | ||
1281 | struct __mux mux; | ||
1282 | struct __queue que; | ||
1283 | struct __ctrl ctrl; | ||
1284 | } region; | ||
1285 | } __packed; | ||
1286 | |||
1287 | enum op_codes { | ||
1288 | QLCNIC_DUMP_NOP = 0, | ||
1289 | QLCNIC_DUMP_READ_CRB = 1, | ||
1290 | QLCNIC_DUMP_READ_MUX = 2, | ||
1291 | QLCNIC_DUMP_QUEUE = 3, | ||
1292 | QLCNIC_DUMP_BRD_CONFIG = 4, | ||
1293 | QLCNIC_DUMP_READ_OCM = 6, | ||
1294 | QLCNIC_DUMP_PEG_REG = 7, | ||
1295 | QLCNIC_DUMP_L1_DTAG = 8, | ||
1296 | QLCNIC_DUMP_L1_ITAG = 9, | ||
1297 | QLCNIC_DUMP_L1_DATA = 11, | ||
1298 | QLCNIC_DUMP_L1_INST = 12, | ||
1299 | QLCNIC_DUMP_L2_DTAG = 21, | ||
1300 | QLCNIC_DUMP_L2_ITAG = 22, | ||
1301 | QLCNIC_DUMP_L2_DATA = 23, | ||
1302 | QLCNIC_DUMP_L2_INST = 24, | ||
1303 | QLCNIC_DUMP_READ_ROM = 71, | ||
1304 | QLCNIC_DUMP_READ_MEM = 72, | ||
1305 | QLCNIC_DUMP_READ_CTRL = 98, | ||
1306 | QLCNIC_DUMP_TLHDR = 99, | ||
1307 | QLCNIC_DUMP_RDEND = 255 | ||
1308 | }; | ||
1309 | |||
1310 | #define QLCNIC_DUMP_WCRB BIT_0 | ||
1311 | #define QLCNIC_DUMP_RWCRB BIT_1 | ||
1312 | #define QLCNIC_DUMP_ANDCRB BIT_2 | ||
1313 | #define QLCNIC_DUMP_ORCRB BIT_3 | ||
1314 | #define QLCNIC_DUMP_POLLCRB BIT_4 | ||
1315 | #define QLCNIC_DUMP_RD_SAVE BIT_5 | ||
1316 | #define QLCNIC_DUMP_WRT_SAVED BIT_6 | ||
1317 | #define QLCNIC_DUMP_MOD_SAVE_ST BIT_7 | ||
1318 | #define QLCNIC_DUMP_SKIP BIT_7 | ||
1319 | |||
1320 | #define QLCNIC_DUMP_MASK_MIN 3 | ||
1321 | #define QLCNIC_DUMP_MASK_DEF 0x0f | ||
1322 | #define QLCNIC_DUMP_MASK_MAX 0xff | ||
1323 | #define QLCNIC_FORCE_FW_DUMP_KEY 0xdeadfeed | ||
1324 | |||
1325 | struct qlcnic_dump_operations { | ||
1326 | enum op_codes opcode; | ||
1327 | u32 (*handler)(struct qlcnic_adapter *, | ||
1328 | struct qlcnic_dump_entry *, u32 *); | ||
1329 | }; | ||
1330 | |||
1331 | int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter); | ||
1160 | int qlcnic_fw_cmd_set_port(struct qlcnic_adapter *adapter, u32 config); | 1332 | int qlcnic_fw_cmd_set_port(struct qlcnic_adapter *adapter, u32 config); |
1161 | 1333 | ||
1162 | u32 qlcnic_hw_read_wx_2M(struct qlcnic_adapter *adapter, ulong off); | 1334 | u32 qlcnic_hw_read_wx_2M(struct qlcnic_adapter *adapter, ulong off); |
@@ -1203,6 +1375,7 @@ int qlcnic_wol_supported(struct qlcnic_adapter *adapter); | |||
1203 | int qlcnic_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate); | 1375 | int qlcnic_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate); |
1204 | void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter); | 1376 | void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter); |
1205 | void qlcnic_delete_lb_filters(struct qlcnic_adapter *adapter); | 1377 | void qlcnic_delete_lb_filters(struct qlcnic_adapter *adapter); |
1378 | int qlcnic_dump_fw(struct qlcnic_adapter *); | ||
1206 | 1379 | ||
1207 | /* Functions from qlcnic_init.c */ | 1380 | /* Functions from qlcnic_init.c */ |
1208 | int qlcnic_load_firmware(struct qlcnic_adapter *adapter); | 1381 | int qlcnic_load_firmware(struct qlcnic_adapter *adapter); |
@@ -1213,7 +1386,7 @@ int qlcnic_pinit_from_rom(struct qlcnic_adapter *adapter); | |||
1213 | int qlcnic_setup_idc_param(struct qlcnic_adapter *adapter); | 1386 | int qlcnic_setup_idc_param(struct qlcnic_adapter *adapter); |
1214 | int qlcnic_check_flash_fw_ver(struct qlcnic_adapter *adapter); | 1387 | int qlcnic_check_flash_fw_ver(struct qlcnic_adapter *adapter); |
1215 | 1388 | ||
1216 | int qlcnic_rom_fast_read(struct qlcnic_adapter *adapter, int addr, int *valp); | 1389 | int qlcnic_rom_fast_read(struct qlcnic_adapter *adapter, u32 addr, u32 *valp); |
1217 | int qlcnic_rom_fast_read_words(struct qlcnic_adapter *adapter, int addr, | 1390 | int qlcnic_rom_fast_read_words(struct qlcnic_adapter *adapter, int addr, |
1218 | u8 *bytes, size_t size); | 1391 | u8 *bytes, size_t size); |
1219 | int qlcnic_alloc_sw_resources(struct qlcnic_adapter *adapter); | 1392 | int qlcnic_alloc_sw_resources(struct qlcnic_adapter *adapter); |
@@ -1265,6 +1438,7 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test); | |||
1265 | netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev); | 1438 | netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev); |
1266 | int qlcnic_validate_max_rss(struct net_device *netdev, u8 max_hw, u8 val); | 1439 | int qlcnic_validate_max_rss(struct net_device *netdev, u8 max_hw, u8 val); |
1267 | int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data); | 1440 | int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data); |
1441 | void qlcnic_dev_request_reset(struct qlcnic_adapter *); | ||
1268 | 1442 | ||
1269 | /* Management functions */ | 1443 | /* Management functions */ |
1270 | int qlcnic_get_mac_address(struct qlcnic_adapter *, u8*); | 1444 | int qlcnic_get_mac_address(struct qlcnic_adapter *, u8*); |
diff --git a/drivers/net/qlcnic/qlcnic_ctx.c b/drivers/net/qlcnic/qlcnic_ctx.c index 3a99886e4736..bab041a5c758 100644 --- a/drivers/net/qlcnic/qlcnic_ctx.c +++ b/drivers/net/qlcnic/qlcnic_ctx.c | |||
@@ -64,6 +64,97 @@ qlcnic_issue_cmd(struct qlcnic_adapter *adapter, | |||
64 | return rcode; | 64 | return rcode; |
65 | } | 65 | } |
66 | 66 | ||
67 | static uint32_t qlcnic_temp_checksum(uint32_t *temp_buffer, u16 temp_size) | ||
68 | { | ||
69 | uint64_t sum = 0; | ||
70 | int count = temp_size / sizeof(uint32_t); | ||
71 | while (count-- > 0) | ||
72 | sum += *temp_buffer++; | ||
73 | while (sum >> 32) | ||
74 | sum = (sum & 0xFFFFFFFF) + (sum >> 32); | ||
75 | return ~sum; | ||
76 | } | ||
77 | |||
78 | int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter) | ||
79 | { | ||
80 | int err, i; | ||
81 | u16 temp_size; | ||
82 | void *tmp_addr; | ||
83 | u32 version, csum, *template, *tmp_buf; | ||
84 | struct qlcnic_hardware_context *ahw; | ||
85 | struct qlcnic_dump_template_hdr *tmpl_hdr, *tmp_tmpl; | ||
86 | dma_addr_t tmp_addr_t = 0; | ||
87 | |||
88 | ahw = adapter->ahw; | ||
89 | err = qlcnic_issue_cmd(adapter, | ||
90 | adapter->ahw->pci_func, | ||
91 | adapter->fw_hal_version, | ||
92 | 0, | ||
93 | 0, | ||
94 | 0, | ||
95 | QLCNIC_CDRP_CMD_TEMP_SIZE); | ||
96 | if (err != QLCNIC_RCODE_SUCCESS) { | ||
97 | err = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET); | ||
98 | dev_err(&adapter->pdev->dev, | ||
99 | "Failed to get template size %d\n", err); | ||
100 | err = -EIO; | ||
101 | return err; | ||
102 | } | ||
103 | version = QLCRD32(adapter, QLCNIC_ARG3_CRB_OFFSET); | ||
104 | temp_size = QLCRD32(adapter, QLCNIC_ARG2_CRB_OFFSET); | ||
105 | if (!temp_size) | ||
106 | return -EIO; | ||
107 | |||
108 | tmp_addr = dma_alloc_coherent(&adapter->pdev->dev, temp_size, | ||
109 | &tmp_addr_t, GFP_KERNEL); | ||
110 | if (!tmp_addr) { | ||
111 | dev_err(&adapter->pdev->dev, | ||
112 | "Can't get memory for FW dump template\n"); | ||
113 | return -ENOMEM; | ||
114 | } | ||
115 | err = qlcnic_issue_cmd(adapter, | ||
116 | adapter->ahw->pci_func, | ||
117 | adapter->fw_hal_version, | ||
118 | LSD(tmp_addr_t), | ||
119 | MSD(tmp_addr_t), | ||
120 | temp_size, | ||
121 | QLCNIC_CDRP_CMD_GET_TEMP_HDR); | ||
122 | |||
123 | if (err != QLCNIC_RCODE_SUCCESS) { | ||
124 | dev_err(&adapter->pdev->dev, | ||
125 | "Failed to get mini dump template header %d\n", err); | ||
126 | err = -EIO; | ||
127 | goto error; | ||
128 | } | ||
129 | tmp_tmpl = (struct qlcnic_dump_template_hdr *) tmp_addr; | ||
130 | csum = qlcnic_temp_checksum((uint32_t *) tmp_addr, temp_size); | ||
131 | if (csum) { | ||
132 | dev_err(&adapter->pdev->dev, | ||
133 | "Template header checksum validation failed\n"); | ||
134 | err = -EIO; | ||
135 | goto error; | ||
136 | } | ||
137 | ahw->fw_dump.tmpl_hdr = vzalloc(temp_size); | ||
138 | if (!ahw->fw_dump.tmpl_hdr) { | ||
139 | err = -EIO; | ||
140 | goto error; | ||
141 | } | ||
142 | tmp_buf = (u32 *) tmp_addr; | ||
143 | template = (u32 *) ahw->fw_dump.tmpl_hdr; | ||
144 | for (i = 0; i < temp_size/sizeof(u32); i++) | ||
145 | *template++ = __le32_to_cpu(*tmp_buf++); | ||
146 | |||
147 | tmpl_hdr = ahw->fw_dump.tmpl_hdr; | ||
148 | if (tmpl_hdr->cap_mask > QLCNIC_DUMP_MASK_DEF && | ||
149 | tmpl_hdr->cap_mask <= QLCNIC_DUMP_MASK_MAX) | ||
150 | tmpl_hdr->drv_cap_mask = tmpl_hdr->cap_mask; | ||
151 | else | ||
152 | tmpl_hdr->drv_cap_mask = QLCNIC_DUMP_MASK_DEF; | ||
153 | error: | ||
154 | dma_free_coherent(&adapter->pdev->dev, temp_size, tmp_addr, tmp_addr_t); | ||
155 | return err; | ||
156 | } | ||
157 | |||
67 | int | 158 | int |
68 | qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu) | 159 | qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu) |
69 | { | 160 | { |
diff --git a/drivers/net/qlcnic/qlcnic_hdr.h b/drivers/net/qlcnic/qlcnic_hdr.h index 726ef555b6bc..d14506f764e0 100644 --- a/drivers/net/qlcnic/qlcnic_hdr.h +++ b/drivers/net/qlcnic/qlcnic_hdr.h | |||
@@ -492,10 +492,10 @@ enum { | |||
492 | 492 | ||
493 | #define TEST_AGT_CTRL (0x00) | 493 | #define TEST_AGT_CTRL (0x00) |
494 | 494 | ||
495 | #define TA_CTL_START 1 | 495 | #define TA_CTL_START BIT_0 |
496 | #define TA_CTL_ENABLE 2 | 496 | #define TA_CTL_ENABLE BIT_1 |
497 | #define TA_CTL_WRITE 4 | 497 | #define TA_CTL_WRITE BIT_2 |
498 | #define TA_CTL_BUSY 8 | 498 | #define TA_CTL_BUSY BIT_3 |
499 | 499 | ||
500 | /* | 500 | /* |
501 | * Register offsets for MN | 501 | * Register offsets for MN |
@@ -765,6 +765,38 @@ struct qlcnic_legacy_intr_set { | |||
765 | #define QLCNIC_MAX_PCI_FUNC 8 | 765 | #define QLCNIC_MAX_PCI_FUNC 8 |
766 | #define QLCNIC_MAX_VLAN_FILTERS 64 | 766 | #define QLCNIC_MAX_VLAN_FILTERS 64 |
767 | 767 | ||
768 | /* FW dump defines */ | ||
769 | #define MIU_TEST_CTR 0x41000090 | ||
770 | #define MIU_TEST_ADDR_LO 0x41000094 | ||
771 | #define MIU_TEST_ADDR_HI 0x41000098 | ||
772 | #define FLASH_ROM_WINDOW 0x42110030 | ||
773 | #define FLASH_ROM_DATA 0x42150000 | ||
774 | |||
775 | static const u32 MIU_TEST_READ_DATA[] = { | ||
776 | 0x410000A8, 0x410000AC, 0x410000B8, 0x410000BC, }; | ||
777 | |||
778 | #define QLCNIC_FW_DUMP_REG1 0x00130060 | ||
779 | #define QLCNIC_FW_DUMP_REG2 0x001e0000 | ||
780 | #define QLCNIC_FLASH_SEM2_LK 0x0013C010 | ||
781 | #define QLCNIC_FLASH_SEM2_ULK 0x0013C014 | ||
782 | #define QLCNIC_FLASH_LOCK_ID 0x001B2100 | ||
783 | |||
784 | #define QLCNIC_RD_DUMP_REG(addr, bar0, data) do { \ | ||
785 | writel((addr & 0xFFFF0000), (void *) (bar0 + \ | ||
786 | QLCNIC_FW_DUMP_REG1)); \ | ||
787 | readl((void *) (bar0 + QLCNIC_FW_DUMP_REG1)); \ | ||
788 | *data = readl((void *) (bar0 + QLCNIC_FW_DUMP_REG2 + \ | ||
789 | LSW(addr))); \ | ||
790 | } while (0) | ||
791 | |||
792 | #define QLCNIC_WR_DUMP_REG(addr, bar0, data) do { \ | ||
793 | writel((addr & 0xFFFF0000), (void *) (bar0 + \ | ||
794 | QLCNIC_FW_DUMP_REG1)); \ | ||
795 | readl((void *) (bar0 + QLCNIC_FW_DUMP_REG1)); \ | ||
796 | writel(data, (void *) (bar0 + QLCNIC_FW_DUMP_REG2 + LSW(addr)));\ | ||
797 | readl((void *) (bar0 + QLCNIC_FW_DUMP_REG2 + LSW(addr))); \ | ||
798 | } while (0) | ||
799 | |||
768 | /* PCI function operational mode */ | 800 | /* PCI function operational mode */ |
769 | enum { | 801 | enum { |
770 | QLCNIC_MGMT_FUNC = 0, | 802 | QLCNIC_MGMT_FUNC = 0, |
diff --git a/drivers/net/qlcnic/qlcnic_hw.c b/drivers/net/qlcnic/qlcnic_hw.c index cbb27f2df008..e9656616f2a2 100644 --- a/drivers/net/qlcnic/qlcnic_hw.c +++ b/drivers/net/qlcnic/qlcnic_hw.c | |||
@@ -9,6 +9,7 @@ | |||
9 | 9 | ||
10 | #include <linux/slab.h> | 10 | #include <linux/slab.h> |
11 | #include <net/ip.h> | 11 | #include <net/ip.h> |
12 | #include <linux/bitops.h> | ||
12 | 13 | ||
13 | #define MASK(n) ((1ULL<<(n))-1) | 14 | #define MASK(n) ((1ULL<<(n))-1) |
14 | #define OCM_WIN_P3P(addr) (addr & 0xffc0000) | 15 | #define OCM_WIN_P3P(addr) (addr & 0xffc0000) |
@@ -1261,3 +1262,461 @@ int qlcnic_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate) | |||
1261 | 1262 | ||
1262 | return rv; | 1263 | return rv; |
1263 | } | 1264 | } |
1265 | |||
1266 | /* FW dump related functions */ | ||
1267 | static u32 | ||
1268 | qlcnic_dump_crb(struct qlcnic_adapter *adapter, struct qlcnic_dump_entry *entry, | ||
1269 | u32 *buffer) | ||
1270 | { | ||
1271 | int i; | ||
1272 | u32 addr, data; | ||
1273 | struct __crb *crb = &entry->region.crb; | ||
1274 | void __iomem *base = adapter->ahw->pci_base0; | ||
1275 | |||
1276 | addr = crb->addr; | ||
1277 | |||
1278 | for (i = 0; i < crb->no_ops; i++) { | ||
1279 | QLCNIC_RD_DUMP_REG(addr, base, &data); | ||
1280 | *buffer++ = cpu_to_le32(addr); | ||
1281 | *buffer++ = cpu_to_le32(data); | ||
1282 | addr += crb->stride; | ||
1283 | } | ||
1284 | return crb->no_ops * 2 * sizeof(u32); | ||
1285 | } | ||
1286 | |||
1287 | static u32 | ||
1288 | qlcnic_dump_ctrl(struct qlcnic_adapter *adapter, | ||
1289 | struct qlcnic_dump_entry *entry, u32 *buffer) | ||
1290 | { | ||
1291 | int i, k, timeout = 0; | ||
1292 | void __iomem *base = adapter->ahw->pci_base0; | ||
1293 | u32 addr, data; | ||
1294 | u8 opcode, no_ops; | ||
1295 | struct __ctrl *ctr = &entry->region.ctrl; | ||
1296 | struct qlcnic_dump_template_hdr *t_hdr = adapter->ahw->fw_dump.tmpl_hdr; | ||
1297 | |||
1298 | addr = ctr->addr; | ||
1299 | no_ops = ctr->no_ops; | ||
1300 | |||
1301 | for (i = 0; i < no_ops; i++) { | ||
1302 | k = 0; | ||
1303 | opcode = 0; | ||
1304 | for (k = 0; k < 8; k++) { | ||
1305 | if (!(ctr->opcode & (1 << k))) | ||
1306 | continue; | ||
1307 | switch (1 << k) { | ||
1308 | case QLCNIC_DUMP_WCRB: | ||
1309 | QLCNIC_WR_DUMP_REG(addr, base, ctr->val1); | ||
1310 | break; | ||
1311 | case QLCNIC_DUMP_RWCRB: | ||
1312 | QLCNIC_RD_DUMP_REG(addr, base, &data); | ||
1313 | QLCNIC_WR_DUMP_REG(addr, base, data); | ||
1314 | break; | ||
1315 | case QLCNIC_DUMP_ANDCRB: | ||
1316 | QLCNIC_RD_DUMP_REG(addr, base, &data); | ||
1317 | QLCNIC_WR_DUMP_REG(addr, base, | ||
1318 | (data & ctr->val2)); | ||
1319 | break; | ||
1320 | case QLCNIC_DUMP_ORCRB: | ||
1321 | QLCNIC_RD_DUMP_REG(addr, base, &data); | ||
1322 | QLCNIC_WR_DUMP_REG(addr, base, | ||
1323 | (data | ctr->val3)); | ||
1324 | break; | ||
1325 | case QLCNIC_DUMP_POLLCRB: | ||
1326 | while (timeout <= ctr->timeout) { | ||
1327 | QLCNIC_RD_DUMP_REG(addr, base, &data); | ||
1328 | if ((data & ctr->val2) == ctr->val1) | ||
1329 | break; | ||
1330 | msleep(1); | ||
1331 | timeout++; | ||
1332 | } | ||
1333 | if (timeout > ctr->timeout) { | ||
1334 | dev_info(&adapter->pdev->dev, | ||
1335 | "Timed out, aborting poll CRB\n"); | ||
1336 | return -EINVAL; | ||
1337 | } | ||
1338 | break; | ||
1339 | case QLCNIC_DUMP_RD_SAVE: | ||
1340 | if (ctr->index_a) | ||
1341 | addr = t_hdr->saved_state[ctr->index_a]; | ||
1342 | QLCNIC_RD_DUMP_REG(addr, base, &data); | ||
1343 | t_hdr->saved_state[ctr->index_v] = data; | ||
1344 | break; | ||
1345 | case QLCNIC_DUMP_WRT_SAVED: | ||
1346 | if (ctr->index_v) | ||
1347 | data = t_hdr->saved_state[ctr->index_v]; | ||
1348 | else | ||
1349 | data = ctr->val1; | ||
1350 | if (ctr->index_a) | ||
1351 | addr = t_hdr->saved_state[ctr->index_a]; | ||
1352 | QLCNIC_WR_DUMP_REG(addr, base, data); | ||
1353 | break; | ||
1354 | case QLCNIC_DUMP_MOD_SAVE_ST: | ||
1355 | data = t_hdr->saved_state[ctr->index_v]; | ||
1356 | data <<= ctr->shl_val; | ||
1357 | data >>= ctr->shr_val; | ||
1358 | if (ctr->val2) | ||
1359 | data &= ctr->val2; | ||
1360 | data |= ctr->val3; | ||
1361 | data += ctr->val1; | ||
1362 | t_hdr->saved_state[ctr->index_v] = data; | ||
1363 | break; | ||
1364 | default: | ||
1365 | dev_info(&adapter->pdev->dev, | ||
1366 | "Unknown opcode\n"); | ||
1367 | break; | ||
1368 | } | ||
1369 | } | ||
1370 | addr += ctr->stride; | ||
1371 | } | ||
1372 | return 0; | ||
1373 | } | ||
1374 | |||
1375 | static u32 | ||
1376 | qlcnic_dump_mux(struct qlcnic_adapter *adapter, struct qlcnic_dump_entry *entry, | ||
1377 | u32 *buffer) | ||
1378 | { | ||
1379 | int loop; | ||
1380 | u32 val, data = 0; | ||
1381 | struct __mux *mux = &entry->region.mux; | ||
1382 | void __iomem *base = adapter->ahw->pci_base0; | ||
1383 | |||
1384 | val = mux->val; | ||
1385 | for (loop = 0; loop < mux->no_ops; loop++) { | ||
1386 | QLCNIC_WR_DUMP_REG(mux->addr, base, val); | ||
1387 | QLCNIC_RD_DUMP_REG(mux->read_addr, base, &data); | ||
1388 | *buffer++ = cpu_to_le32(val); | ||
1389 | *buffer++ = cpu_to_le32(data); | ||
1390 | val += mux->val_stride; | ||
1391 | } | ||
1392 | return 2 * mux->no_ops * sizeof(u32); | ||
1393 | } | ||
1394 | |||
1395 | static u32 | ||
1396 | qlcnic_dump_que(struct qlcnic_adapter *adapter, struct qlcnic_dump_entry *entry, | ||
1397 | u32 *buffer) | ||
1398 | { | ||
1399 | int i, loop; | ||
1400 | u32 cnt, addr, data, que_id = 0; | ||
1401 | void __iomem *base = adapter->ahw->pci_base0; | ||
1402 | struct __queue *que = &entry->region.que; | ||
1403 | |||
1404 | addr = que->read_addr; | ||
1405 | cnt = que->read_addr_cnt; | ||
1406 | |||
1407 | for (loop = 0; loop < que->no_ops; loop++) { | ||
1408 | QLCNIC_WR_DUMP_REG(que->sel_addr, base, que_id); | ||
1409 | for (i = 0; i < cnt; i++) { | ||
1410 | QLCNIC_RD_DUMP_REG(addr, base, &data); | ||
1411 | *buffer++ = cpu_to_le32(data); | ||
1412 | addr += que->read_addr_stride; | ||
1413 | } | ||
1414 | que_id += que->stride; | ||
1415 | } | ||
1416 | return que->no_ops * cnt * sizeof(u32); | ||
1417 | } | ||
1418 | |||
1419 | static u32 | ||
1420 | qlcnic_dump_ocm(struct qlcnic_adapter *adapter, struct qlcnic_dump_entry *entry, | ||
1421 | u32 *buffer) | ||
1422 | { | ||
1423 | int i; | ||
1424 | u32 data; | ||
1425 | void __iomem *addr; | ||
1426 | struct __ocm *ocm = &entry->region.ocm; | ||
1427 | |||
1428 | addr = adapter->ahw->pci_base0 + ocm->read_addr; | ||
1429 | for (i = 0; i < ocm->no_ops; i++) { | ||
1430 | data = readl(addr); | ||
1431 | *buffer++ = cpu_to_le32(data); | ||
1432 | addr += ocm->read_addr_stride; | ||
1433 | } | ||
1434 | return ocm->no_ops * sizeof(u32); | ||
1435 | } | ||
1436 | |||
1437 | static u32 | ||
1438 | qlcnic_read_rom(struct qlcnic_adapter *adapter, struct qlcnic_dump_entry *entry, | ||
1439 | u32 *buffer) | ||
1440 | { | ||
1441 | int i, count = 0; | ||
1442 | u32 fl_addr, size, val, lck_val, addr; | ||
1443 | struct __mem *rom = &entry->region.mem; | ||
1444 | void __iomem *base = adapter->ahw->pci_base0; | ||
1445 | |||
1446 | fl_addr = rom->addr; | ||
1447 | size = rom->size/4; | ||
1448 | lock_try: | ||
1449 | lck_val = readl(base + QLCNIC_FLASH_SEM2_LK); | ||
1450 | if (!lck_val && count < MAX_CTL_CHECK) { | ||
1451 | msleep(10); | ||
1452 | count++; | ||
1453 | goto lock_try; | ||
1454 | } | ||
1455 | writel(adapter->ahw->pci_func, (base + QLCNIC_FLASH_LOCK_ID)); | ||
1456 | for (i = 0; i < size; i++) { | ||
1457 | addr = fl_addr & 0xFFFF0000; | ||
1458 | QLCNIC_WR_DUMP_REG(FLASH_ROM_WINDOW, base, addr); | ||
1459 | addr = LSW(fl_addr) + FLASH_ROM_DATA; | ||
1460 | QLCNIC_RD_DUMP_REG(addr, base, &val); | ||
1461 | fl_addr += 4; | ||
1462 | *buffer++ = cpu_to_le32(val); | ||
1463 | } | ||
1464 | readl(base + QLCNIC_FLASH_SEM2_ULK); | ||
1465 | return rom->size; | ||
1466 | } | ||
1467 | |||
1468 | static u32 | ||
1469 | qlcnic_dump_l1_cache(struct qlcnic_adapter *adapter, | ||
1470 | struct qlcnic_dump_entry *entry, u32 *buffer) | ||
1471 | { | ||
1472 | int i; | ||
1473 | u32 cnt, val, data, addr; | ||
1474 | void __iomem *base = adapter->ahw->pci_base0; | ||
1475 | struct __cache *l1 = &entry->region.cache; | ||
1476 | |||
1477 | val = l1->init_tag_val; | ||
1478 | |||
1479 | for (i = 0; i < l1->no_ops; i++) { | ||
1480 | QLCNIC_WR_DUMP_REG(l1->addr, base, val); | ||
1481 | QLCNIC_WR_DUMP_REG(l1->ctrl_addr, base, LSW(l1->ctrl_val)); | ||
1482 | addr = l1->read_addr; | ||
1483 | cnt = l1->read_addr_num; | ||
1484 | while (cnt) { | ||
1485 | QLCNIC_RD_DUMP_REG(addr, base, &data); | ||
1486 | *buffer++ = cpu_to_le32(data); | ||
1487 | addr += l1->read_addr_stride; | ||
1488 | cnt--; | ||
1489 | } | ||
1490 | val += l1->stride; | ||
1491 | } | ||
1492 | return l1->no_ops * l1->read_addr_num * sizeof(u32); | ||
1493 | } | ||
1494 | |||
1495 | static u32 | ||
1496 | qlcnic_dump_l2_cache(struct qlcnic_adapter *adapter, | ||
1497 | struct qlcnic_dump_entry *entry, u32 *buffer) | ||
1498 | { | ||
1499 | int i; | ||
1500 | u32 cnt, val, data, addr; | ||
1501 | u8 poll_mask, poll_to, time_out = 0; | ||
1502 | void __iomem *base = adapter->ahw->pci_base0; | ||
1503 | struct __cache *l2 = &entry->region.cache; | ||
1504 | |||
1505 | val = l2->init_tag_val; | ||
1506 | poll_mask = LSB(MSW(l2->ctrl_val)); | ||
1507 | poll_to = MSB(MSW(l2->ctrl_val)); | ||
1508 | |||
1509 | for (i = 0; i < l2->no_ops; i++) { | ||
1510 | QLCNIC_WR_DUMP_REG(l2->addr, base, val); | ||
1511 | do { | ||
1512 | QLCNIC_WR_DUMP_REG(l2->ctrl_addr, base, | ||
1513 | LSW(l2->ctrl_val)); | ||
1514 | QLCNIC_RD_DUMP_REG(l2->ctrl_addr, base, &data); | ||
1515 | if (!(data & poll_mask)) | ||
1516 | break; | ||
1517 | msleep(1); | ||
1518 | time_out++; | ||
1519 | } while (time_out <= poll_to); | ||
1520 | if (time_out > poll_to) | ||
1521 | return -EINVAL; | ||
1522 | |||
1523 | addr = l2->read_addr; | ||
1524 | cnt = l2->read_addr_num; | ||
1525 | while (cnt) { | ||
1526 | QLCNIC_RD_DUMP_REG(addr, base, &data); | ||
1527 | *buffer++ = cpu_to_le32(data); | ||
1528 | addr += l2->read_addr_stride; | ||
1529 | cnt--; | ||
1530 | } | ||
1531 | val += l2->stride; | ||
1532 | } | ||
1533 | return l2->no_ops * l2->read_addr_num * sizeof(u32); | ||
1534 | } | ||
1535 | |||
1536 | static u32 | ||
1537 | qlcnic_read_memory(struct qlcnic_adapter *adapter, | ||
1538 | struct qlcnic_dump_entry *entry, u32 *buffer) | ||
1539 | { | ||
1540 | u32 addr, data, test, ret = 0; | ||
1541 | int i, reg_read; | ||
1542 | struct __mem *mem = &entry->region.mem; | ||
1543 | void __iomem *base = adapter->ahw->pci_base0; | ||
1544 | |||
1545 | reg_read = mem->size; | ||
1546 | addr = mem->addr; | ||
1547 | /* check for data size of multiple of 16 and 16 byte alignment */ | ||
1548 | if ((addr & 0xf) || (reg_read%16)) { | ||
1549 | dev_info(&adapter->pdev->dev, | ||
1550 | "Unaligned memory addr:0x%x size:0x%x\n", | ||
1551 | addr, reg_read); | ||
1552 | return -EINVAL; | ||
1553 | } | ||
1554 | |||
1555 | mutex_lock(&adapter->ahw->mem_lock); | ||
1556 | |||
1557 | while (reg_read != 0) { | ||
1558 | QLCNIC_WR_DUMP_REG(MIU_TEST_ADDR_LO, base, addr); | ||
1559 | QLCNIC_WR_DUMP_REG(MIU_TEST_ADDR_HI, base, 0); | ||
1560 | QLCNIC_WR_DUMP_REG(MIU_TEST_CTR, base, | ||
1561 | TA_CTL_ENABLE | TA_CTL_START); | ||
1562 | |||
1563 | for (i = 0; i < MAX_CTL_CHECK; i++) { | ||
1564 | QLCNIC_RD_DUMP_REG(MIU_TEST_CTR, base, &test); | ||
1565 | if (!(test & TA_CTL_BUSY)) | ||
1566 | break; | ||
1567 | } | ||
1568 | if (i == MAX_CTL_CHECK) { | ||
1569 | if (printk_ratelimit()) { | ||
1570 | dev_err(&adapter->pdev->dev, | ||
1571 | "failed to read through agent\n"); | ||
1572 | ret = -EINVAL; | ||
1573 | goto out; | ||
1574 | } | ||
1575 | } | ||
1576 | for (i = 0; i < 4; i++) { | ||
1577 | QLCNIC_RD_DUMP_REG(MIU_TEST_READ_DATA[i], base, &data); | ||
1578 | *buffer++ = cpu_to_le32(data); | ||
1579 | } | ||
1580 | addr += 16; | ||
1581 | reg_read -= 16; | ||
1582 | ret += 16; | ||
1583 | } | ||
1584 | out: | ||
1585 | mutex_unlock(&adapter->ahw->mem_lock); | ||
1586 | return mem->size; | ||
1587 | } | ||
1588 | |||
1589 | static u32 | ||
1590 | qlcnic_dump_nop(struct qlcnic_adapter *adapter, | ||
1591 | struct qlcnic_dump_entry *entry, u32 *buffer) | ||
1592 | { | ||
1593 | entry->hdr.flags |= QLCNIC_DUMP_SKIP; | ||
1594 | return 0; | ||
1595 | } | ||
1596 | |||
1597 | struct qlcnic_dump_operations fw_dump_ops[] = { | ||
1598 | { QLCNIC_DUMP_NOP, qlcnic_dump_nop }, | ||
1599 | { QLCNIC_DUMP_READ_CRB, qlcnic_dump_crb }, | ||
1600 | { QLCNIC_DUMP_READ_MUX, qlcnic_dump_mux }, | ||
1601 | { QLCNIC_DUMP_QUEUE, qlcnic_dump_que }, | ||
1602 | { QLCNIC_DUMP_BRD_CONFIG, qlcnic_read_rom }, | ||
1603 | { QLCNIC_DUMP_READ_OCM, qlcnic_dump_ocm }, | ||
1604 | { QLCNIC_DUMP_PEG_REG, qlcnic_dump_ctrl }, | ||
1605 | { QLCNIC_DUMP_L1_DTAG, qlcnic_dump_l1_cache }, | ||
1606 | { QLCNIC_DUMP_L1_ITAG, qlcnic_dump_l1_cache }, | ||
1607 | { QLCNIC_DUMP_L1_DATA, qlcnic_dump_l1_cache }, | ||
1608 | { QLCNIC_DUMP_L1_INST, qlcnic_dump_l1_cache }, | ||
1609 | { QLCNIC_DUMP_L2_DTAG, qlcnic_dump_l2_cache }, | ||
1610 | { QLCNIC_DUMP_L2_ITAG, qlcnic_dump_l2_cache }, | ||
1611 | { QLCNIC_DUMP_L2_DATA, qlcnic_dump_l2_cache }, | ||
1612 | { QLCNIC_DUMP_L2_INST, qlcnic_dump_l2_cache }, | ||
1613 | { QLCNIC_DUMP_READ_ROM, qlcnic_read_rom }, | ||
1614 | { QLCNIC_DUMP_READ_MEM, qlcnic_read_memory }, | ||
1615 | { QLCNIC_DUMP_READ_CTRL, qlcnic_dump_ctrl }, | ||
1616 | { QLCNIC_DUMP_TLHDR, qlcnic_dump_nop }, | ||
1617 | { QLCNIC_DUMP_RDEND, qlcnic_dump_nop }, | ||
1618 | }; | ||
1619 | |||
1620 | /* Walk the template and collect dump for each entry in the dump template */ | ||
1621 | static int | ||
1622 | qlcnic_valid_dump_entry(struct device *dev, struct qlcnic_dump_entry *entry, | ||
1623 | u32 size) | ||
1624 | { | ||
1625 | int ret = 1; | ||
1626 | if (size != entry->hdr.cap_size) { | ||
1627 | dev_info(dev, | ||
1628 | "Invalidate dump, Type:%d\tMask:%d\tSize:%dCap_size:%d\n", | ||
1629 | entry->hdr.type, entry->hdr.mask, size, entry->hdr.cap_size); | ||
1630 | dev_info(dev, "Aborting further dump capture\n"); | ||
1631 | ret = 0; | ||
1632 | } | ||
1633 | return ret; | ||
1634 | } | ||
1635 | |||
1636 | int qlcnic_dump_fw(struct qlcnic_adapter *adapter) | ||
1637 | { | ||
1638 | u32 *buffer; | ||
1639 | char mesg[64]; | ||
1640 | char *msg[] = {mesg, NULL}; | ||
1641 | int i, k, ops_cnt, ops_index, dump_size = 0; | ||
1642 | u32 entry_offset, dump, no_entries, buf_offset = 0; | ||
1643 | struct qlcnic_dump_entry *entry; | ||
1644 | struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; | ||
1645 | struct qlcnic_dump_template_hdr *tmpl_hdr = fw_dump->tmpl_hdr; | ||
1646 | |||
1647 | if (fw_dump->clr) { | ||
1648 | dev_info(&adapter->pdev->dev, | ||
1649 | "Previous dump not cleared, not capturing dump\n"); | ||
1650 | return -EIO; | ||
1651 | } | ||
1652 | /* Calculate the size for dump data area only */ | ||
1653 | for (i = 2, k = 1; (i & QLCNIC_DUMP_MASK_MAX); i <<= 1, k++) | ||
1654 | if (i & tmpl_hdr->drv_cap_mask) | ||
1655 | dump_size += tmpl_hdr->cap_sizes[k]; | ||
1656 | if (!dump_size) | ||
1657 | return -EIO; | ||
1658 | |||
1659 | fw_dump->data = vzalloc(dump_size); | ||
1660 | if (!fw_dump->data) { | ||
1661 | dev_info(&adapter->pdev->dev, | ||
1662 | "Unable to allocate (%d KB) for fw dump\n", | ||
1663 | dump_size/1024); | ||
1664 | return -ENOMEM; | ||
1665 | } | ||
1666 | buffer = fw_dump->data; | ||
1667 | fw_dump->size = dump_size; | ||
1668 | no_entries = tmpl_hdr->num_entries; | ||
1669 | ops_cnt = ARRAY_SIZE(fw_dump_ops); | ||
1670 | entry_offset = tmpl_hdr->offset; | ||
1671 | tmpl_hdr->sys_info[0] = QLCNIC_DRIVER_VERSION; | ||
1672 | tmpl_hdr->sys_info[1] = adapter->fw_version; | ||
1673 | |||
1674 | for (i = 0; i < no_entries; i++) { | ||
1675 | entry = (struct qlcnic_dump_entry *) ((void *) tmpl_hdr + | ||
1676 | entry_offset); | ||
1677 | if (!(entry->hdr.mask & tmpl_hdr->drv_cap_mask)) { | ||
1678 | entry->hdr.flags |= QLCNIC_DUMP_SKIP; | ||
1679 | entry_offset += entry->hdr.offset; | ||
1680 | continue; | ||
1681 | } | ||
1682 | /* Find the handler for this entry */ | ||
1683 | ops_index = 0; | ||
1684 | while (ops_index < ops_cnt) { | ||
1685 | if (entry->hdr.type == fw_dump_ops[ops_index].opcode) | ||
1686 | break; | ||
1687 | ops_index++; | ||
1688 | } | ||
1689 | if (ops_index == ops_cnt) { | ||
1690 | dev_info(&adapter->pdev->dev, | ||
1691 | "Invalid entry type %d, exiting dump\n", | ||
1692 | entry->hdr.type); | ||
1693 | goto error; | ||
1694 | } | ||
1695 | /* Collect dump for this entry */ | ||
1696 | dump = fw_dump_ops[ops_index].handler(adapter, entry, buffer); | ||
1697 | if (dump && !qlcnic_valid_dump_entry(&adapter->pdev->dev, entry, | ||
1698 | dump)) | ||
1699 | entry->hdr.flags |= QLCNIC_DUMP_SKIP; | ||
1700 | buf_offset += entry->hdr.cap_size; | ||
1701 | entry_offset += entry->hdr.offset; | ||
1702 | buffer = fw_dump->data + buf_offset; | ||
1703 | } | ||
1704 | if (dump_size != buf_offset) { | ||
1705 | dev_info(&adapter->pdev->dev, | ||
1706 | "Captured(%d) and expected size(%d) do not match\n", | ||
1707 | buf_offset, dump_size); | ||
1708 | goto error; | ||
1709 | } else { | ||
1710 | fw_dump->clr = 1; | ||
1711 | snprintf(mesg, sizeof(mesg), "FW dump for device: %d\n", | ||
1712 | adapter->pdev->devfn); | ||
1713 | dev_info(&adapter->pdev->dev, "Dump data, %d bytes captured\n", | ||
1714 | fw_dump->size); | ||
1715 | /* Send a udev event to notify availability of FW dump */ | ||
1716 | kobject_uevent_env(&adapter->pdev->dev.kobj, KOBJ_CHANGE, msg); | ||
1717 | return 0; | ||
1718 | } | ||
1719 | error: | ||
1720 | vfree(fw_dump->data); | ||
1721 | return -EINVAL; | ||
1722 | } | ||
diff --git a/drivers/net/qlcnic/qlcnic_init.c b/drivers/net/qlcnic/qlcnic_init.c index d0f338b01396..5b8bbcf904d5 100644 --- a/drivers/net/qlcnic/qlcnic_init.c +++ b/drivers/net/qlcnic/qlcnic_init.c | |||
@@ -345,7 +345,7 @@ static int qlcnic_wait_rom_done(struct qlcnic_adapter *adapter) | |||
345 | } | 345 | } |
346 | 346 | ||
347 | static int do_rom_fast_read(struct qlcnic_adapter *adapter, | 347 | static int do_rom_fast_read(struct qlcnic_adapter *adapter, |
348 | int addr, int *valp) | 348 | u32 addr, u32 *valp) |
349 | { | 349 | { |
350 | QLCWR32(adapter, QLCNIC_ROMUSB_ROM_ADDRESS, addr); | 350 | QLCWR32(adapter, QLCNIC_ROMUSB_ROM_ADDRESS, addr); |
351 | QLCWR32(adapter, QLCNIC_ROMUSB_ROM_DUMMY_BYTE_CNT, 0); | 351 | QLCWR32(adapter, QLCNIC_ROMUSB_ROM_DUMMY_BYTE_CNT, 0); |
@@ -398,7 +398,7 @@ qlcnic_rom_fast_read_words(struct qlcnic_adapter *adapter, int addr, | |||
398 | return ret; | 398 | return ret; |
399 | } | 399 | } |
400 | 400 | ||
401 | int qlcnic_rom_fast_read(struct qlcnic_adapter *adapter, int addr, int *valp) | 401 | int qlcnic_rom_fast_read(struct qlcnic_adapter *adapter, u32 addr, u32 *valp) |
402 | { | 402 | { |
403 | int ret; | 403 | int ret; |
404 | 404 | ||
diff --git a/drivers/net/qlcnic/qlcnic_main.c b/drivers/net/qlcnic/qlcnic_main.c index d6cc4d479591..3ab7d2c7baf2 100644 --- a/drivers/net/qlcnic/qlcnic_main.c +++ b/drivers/net/qlcnic/qlcnic_main.c | |||
@@ -1343,6 +1343,10 @@ static void qlcnic_free_adapter_resources(struct qlcnic_adapter *adapter) | |||
1343 | kfree(adapter->recv_ctx); | 1343 | kfree(adapter->recv_ctx); |
1344 | adapter->recv_ctx = NULL; | 1344 | adapter->recv_ctx = NULL; |
1345 | 1345 | ||
1346 | if (adapter->ahw->fw_dump.tmpl_hdr) { | ||
1347 | vfree(adapter->ahw->fw_dump.tmpl_hdr); | ||
1348 | adapter->ahw->fw_dump.tmpl_hdr = NULL; | ||
1349 | } | ||
1346 | kfree(adapter->ahw); | 1350 | kfree(adapter->ahw); |
1347 | adapter->ahw = NULL; | 1351 | adapter->ahw = NULL; |
1348 | } | 1352 | } |
@@ -1586,6 +1590,10 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
1586 | /* This will be reset for mezz cards */ | 1590 | /* This will be reset for mezz cards */ |
1587 | adapter->portnum = adapter->ahw->pci_func; | 1591 | adapter->portnum = adapter->ahw->pci_func; |
1588 | 1592 | ||
1593 | /* Get FW dump template and store it */ | ||
1594 | if (adapter->op_mode != QLCNIC_NON_PRIV_FUNC) | ||
1595 | qlcnic_fw_cmd_get_minidump_temp(adapter); | ||
1596 | |||
1589 | err = qlcnic_get_board_info(adapter); | 1597 | err = qlcnic_get_board_info(adapter); |
1590 | if (err) { | 1598 | if (err) { |
1591 | dev_err(&pdev->dev, "Error getting board config info.\n"); | 1599 | dev_err(&pdev->dev, "Error getting board config info.\n"); |
@@ -2825,6 +2833,8 @@ skip_ack_check: | |||
2825 | set_bit(__QLCNIC_START_FW, &adapter->state); | 2833 | set_bit(__QLCNIC_START_FW, &adapter->state); |
2826 | QLCDB(adapter, DRV, "Restarting fw\n"); | 2834 | QLCDB(adapter, DRV, "Restarting fw\n"); |
2827 | qlcnic_idc_debug_info(adapter, 0); | 2835 | qlcnic_idc_debug_info(adapter, 0); |
2836 | QLCDB(adapter, DRV, "Take FW dump\n"); | ||
2837 | qlcnic_dump_fw(adapter); | ||
2828 | } | 2838 | } |
2829 | 2839 | ||
2830 | qlcnic_api_unlock(adapter); | 2840 | qlcnic_api_unlock(adapter); |
@@ -2923,7 +2933,7 @@ qlcnic_set_npar_non_operational(struct qlcnic_adapter *adapter) | |||
2923 | } | 2933 | } |
2924 | 2934 | ||
2925 | /*Transit to RESET state from READY state only */ | 2935 | /*Transit to RESET state from READY state only */ |
2926 | static void | 2936 | void |
2927 | qlcnic_dev_request_reset(struct qlcnic_adapter *adapter) | 2937 | qlcnic_dev_request_reset(struct qlcnic_adapter *adapter) |
2928 | { | 2938 | { |
2929 | u32 state; | 2939 | u32 state; |
@@ -3515,7 +3525,6 @@ qlcnic_sysfs_write_mem(struct file *filp, struct kobject *kobj, | |||
3515 | return size; | 3525 | return size; |
3516 | } | 3526 | } |
3517 | 3527 | ||
3518 | |||
3519 | static struct bin_attribute bin_attr_crb = { | 3528 | static struct bin_attribute bin_attr_crb = { |
3520 | .attr = {.name = "crb", .mode = (S_IRUGO | S_IWUSR)}, | 3529 | .attr = {.name = "crb", .mode = (S_IRUGO | S_IWUSR)}, |
3521 | .size = 0, | 3530 | .size = 0, |