diff options
| -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, |
