diff options
author | Shahed Shaikh <shahed.shaikh@qlogic.com> | 2014-02-21 13:20:10 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-02-24 18:43:19 -0500 |
commit | 225837a076b284ac408a3b0104584ae0ad117a0c (patch) | |
tree | 406d15ae01fd9e27f9d3d69c2663841a4ba344fd /drivers/net/ethernet/qlogic | |
parent | a1991c749a7bfacb766a056aff11864216670dd6 (diff) |
qlcnic: Re-factor firmware minidump template header handling
Treat firmware minidump template headers for 82xx and 83xx/84xx adapters separately,
as it may change for 82xx and 83xx/84xx adapter type independently.
Signed-off-by: Shahed Shaikh <shahed.shaikh@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/qlogic')
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 73 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 7 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h | 8 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c | 17 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h | 8 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 7 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c | 233 |
7 files changed, 287 insertions, 66 deletions
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index f19f81cde134..6ad1bcebc0fe 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | |||
@@ -394,7 +394,7 @@ struct qlcnic_nic_intr_coalesce { | |||
394 | u32 timer_out; | 394 | u32 timer_out; |
395 | }; | 395 | }; |
396 | 396 | ||
397 | struct qlcnic_dump_template_hdr { | 397 | struct qlcnic_83xx_dump_template_hdr { |
398 | u32 type; | 398 | u32 type; |
399 | u32 offset; | 399 | u32 offset; |
400 | u32 size; | 400 | u32 size; |
@@ -411,15 +411,42 @@ struct qlcnic_dump_template_hdr { | |||
411 | u32 rsvd[0]; | 411 | u32 rsvd[0]; |
412 | }; | 412 | }; |
413 | 413 | ||
414 | struct qlcnic_82xx_dump_template_hdr { | ||
415 | u32 type; | ||
416 | u32 offset; | ||
417 | u32 size; | ||
418 | u32 cap_mask; | ||
419 | u32 num_entries; | ||
420 | u32 version; | ||
421 | u32 timestamp; | ||
422 | u32 checksum; | ||
423 | u32 drv_cap_mask; | ||
424 | u32 sys_info[3]; | ||
425 | u32 saved_state[16]; | ||
426 | u32 cap_sizes[8]; | ||
427 | u32 rsvd[7]; | ||
428 | u32 capabilities; | ||
429 | u32 rsvd1[0]; | ||
430 | }; | ||
431 | |||
414 | struct qlcnic_fw_dump { | 432 | struct qlcnic_fw_dump { |
415 | u8 clr; /* flag to indicate if dump is cleared */ | 433 | u8 clr; /* flag to indicate if dump is cleared */ |
416 | bool enable; /* enable/disable dump */ | 434 | bool enable; /* enable/disable dump */ |
417 | u32 size; /* total size of the dump */ | 435 | u32 size; /* total size of the dump */ |
436 | u32 cap_mask; /* Current capture mask */ | ||
418 | void *data; /* dump data area */ | 437 | void *data; /* dump data area */ |
419 | struct qlcnic_dump_template_hdr *tmpl_hdr; | 438 | void *tmpl_hdr; |
420 | dma_addr_t phys_addr; | 439 | dma_addr_t phys_addr; |
421 | void *dma_buffer; | 440 | void *dma_buffer; |
422 | bool use_pex_dma; | 441 | bool use_pex_dma; |
442 | /* Read only elements which are common between 82xx and 83xx | ||
443 | * template header. Update these values immediately after we read | ||
444 | * template header from Firmware | ||
445 | */ | ||
446 | u32 tmpl_hdr_size; | ||
447 | u32 version; | ||
448 | u32 num_entries; | ||
449 | u32 offset; | ||
423 | }; | 450 | }; |
424 | 451 | ||
425 | /* | 452 | /* |
@@ -1769,6 +1796,12 @@ struct qlcnic_hardware_ops { | |||
1769 | struct qlcnic_host_tx_ring *); | 1796 | struct qlcnic_host_tx_ring *); |
1770 | void (*disable_tx_intr) (struct qlcnic_adapter *, | 1797 | void (*disable_tx_intr) (struct qlcnic_adapter *, |
1771 | struct qlcnic_host_tx_ring *); | 1798 | struct qlcnic_host_tx_ring *); |
1799 | u32 (*get_saved_state)(void *, u32); | ||
1800 | void (*set_saved_state)(void *, u32, u32); | ||
1801 | void (*cache_tmpl_hdr_values)(struct qlcnic_fw_dump *); | ||
1802 | u32 (*get_cap_size)(void *, int); | ||
1803 | void (*set_sys_info)(void *, int, u32); | ||
1804 | void (*store_cap_mask)(void *, u32); | ||
1772 | }; | 1805 | }; |
1773 | 1806 | ||
1774 | extern struct qlcnic_nic_template qlcnic_vf_ops; | 1807 | extern struct qlcnic_nic_template qlcnic_vf_ops; |
@@ -2007,6 +2040,42 @@ static inline void qlcnic_read_phys_port_id(struct qlcnic_adapter *adapter) | |||
2007 | adapter->ahw->hw_ops->read_phys_port_id(adapter); | 2040 | adapter->ahw->hw_ops->read_phys_port_id(adapter); |
2008 | } | 2041 | } |
2009 | 2042 | ||
2043 | static inline u32 qlcnic_get_saved_state(struct qlcnic_adapter *adapter, | ||
2044 | void *t_hdr, u32 index) | ||
2045 | { | ||
2046 | return adapter->ahw->hw_ops->get_saved_state(t_hdr, index); | ||
2047 | } | ||
2048 | |||
2049 | static inline void qlcnic_set_saved_state(struct qlcnic_adapter *adapter, | ||
2050 | void *t_hdr, u32 index, u32 value) | ||
2051 | { | ||
2052 | adapter->ahw->hw_ops->set_saved_state(t_hdr, index, value); | ||
2053 | } | ||
2054 | |||
2055 | static inline void qlcnic_cache_tmpl_hdr_values(struct qlcnic_adapter *adapter, | ||
2056 | struct qlcnic_fw_dump *fw_dump) | ||
2057 | { | ||
2058 | adapter->ahw->hw_ops->cache_tmpl_hdr_values(fw_dump); | ||
2059 | } | ||
2060 | |||
2061 | static inline u32 qlcnic_get_cap_size(struct qlcnic_adapter *adapter, | ||
2062 | void *tmpl_hdr, int index) | ||
2063 | { | ||
2064 | return adapter->ahw->hw_ops->get_cap_size(tmpl_hdr, index); | ||
2065 | } | ||
2066 | |||
2067 | static inline void qlcnic_set_sys_info(struct qlcnic_adapter *adapter, | ||
2068 | void *tmpl_hdr, int idx, u32 value) | ||
2069 | { | ||
2070 | adapter->ahw->hw_ops->set_sys_info(tmpl_hdr, idx, value); | ||
2071 | } | ||
2072 | |||
2073 | static inline void qlcnic_store_cap_mask(struct qlcnic_adapter *adapter, | ||
2074 | void *tmpl_hdr, u32 mask) | ||
2075 | { | ||
2076 | adapter->ahw->hw_ops->store_cap_mask(tmpl_hdr, mask); | ||
2077 | } | ||
2078 | |||
2010 | static inline void qlcnic_dev_request_reset(struct qlcnic_adapter *adapter, | 2079 | static inline void qlcnic_dev_request_reset(struct qlcnic_adapter *adapter, |
2011 | u32 key) | 2080 | u32 key) |
2012 | { | 2081 | { |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index 4146664d4d6a..0f39778e0e5b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | |||
@@ -203,7 +203,12 @@ static struct qlcnic_hardware_ops qlcnic_83xx_hw_ops = { | |||
203 | .disable_sds_intr = qlcnic_83xx_disable_sds_intr, | 203 | .disable_sds_intr = qlcnic_83xx_disable_sds_intr, |
204 | .enable_tx_intr = qlcnic_83xx_enable_tx_intr, | 204 | .enable_tx_intr = qlcnic_83xx_enable_tx_intr, |
205 | .disable_tx_intr = qlcnic_83xx_disable_tx_intr, | 205 | .disable_tx_intr = qlcnic_83xx_disable_tx_intr, |
206 | 206 | .get_saved_state = qlcnic_83xx_get_saved_state, | |
207 | .set_saved_state = qlcnic_83xx_set_saved_state, | ||
208 | .cache_tmpl_hdr_values = qlcnic_83xx_cache_tmpl_hdr_values, | ||
209 | .get_cap_size = qlcnic_83xx_get_cap_size, | ||
210 | .set_sys_info = qlcnic_83xx_set_sys_info, | ||
211 | .store_cap_mask = qlcnic_83xx_store_cap_mask, | ||
207 | }; | 212 | }; |
208 | 213 | ||
209 | static struct qlcnic_nic_template qlcnic_83xx_ops = { | 214 | static struct qlcnic_nic_template qlcnic_83xx_ops = { |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h index f92485ca21d1..81c1889f6f3e 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h | |||
@@ -308,6 +308,8 @@ struct qlc_83xx_reset { | |||
308 | #define QLC_83XX_IDC_FLASH_PARAM_ADDR 0x3e8020 | 308 | #define QLC_83XX_IDC_FLASH_PARAM_ADDR 0x3e8020 |
309 | 309 | ||
310 | struct qlcnic_adapter; | 310 | struct qlcnic_adapter; |
311 | struct qlcnic_fw_dump; | ||
312 | |||
311 | struct qlc_83xx_idc { | 313 | struct qlc_83xx_idc { |
312 | int (*state_entry) (struct qlcnic_adapter *); | 314 | int (*state_entry) (struct qlcnic_adapter *); |
313 | u64 sec_counter; | 315 | u64 sec_counter; |
@@ -650,4 +652,10 @@ int qlcnic_83xx_check_vnic_state(struct qlcnic_adapter *); | |||
650 | void qlcnic_83xx_aer_stop_poll_work(struct qlcnic_adapter *); | 652 | void qlcnic_83xx_aer_stop_poll_work(struct qlcnic_adapter *); |
651 | int qlcnic_83xx_aer_reset(struct qlcnic_adapter *); | 653 | int qlcnic_83xx_aer_reset(struct qlcnic_adapter *); |
652 | void qlcnic_83xx_aer_start_poll_work(struct qlcnic_adapter *); | 654 | void qlcnic_83xx_aer_start_poll_work(struct qlcnic_adapter *); |
655 | u32 qlcnic_83xx_get_saved_state(void *, u32); | ||
656 | void qlcnic_83xx_set_saved_state(void *, u32, u32); | ||
657 | void qlcnic_83xx_cache_tmpl_hdr_values(struct qlcnic_fw_dump *); | ||
658 | u32 qlcnic_83xx_get_cap_size(void *, int); | ||
659 | void qlcnic_83xx_set_sys_info(void *, int, u32); | ||
660 | void qlcnic_83xx_store_cap_mask(void *, u32); | ||
653 | #endif | 661 | #endif |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c index acee1a5d80c6..1960609481ce 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c | |||
@@ -1639,14 +1639,14 @@ qlcnic_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump) | |||
1639 | } | 1639 | } |
1640 | 1640 | ||
1641 | if (fw_dump->clr) | 1641 | if (fw_dump->clr) |
1642 | dump->len = fw_dump->tmpl_hdr->size + fw_dump->size; | 1642 | dump->len = fw_dump->tmpl_hdr_size + fw_dump->size; |
1643 | else | 1643 | else |
1644 | dump->len = 0; | 1644 | dump->len = 0; |
1645 | 1645 | ||
1646 | if (!qlcnic_check_fw_dump_state(adapter)) | 1646 | if (!qlcnic_check_fw_dump_state(adapter)) |
1647 | dump->flag = ETH_FW_DUMP_DISABLE; | 1647 | dump->flag = ETH_FW_DUMP_DISABLE; |
1648 | else | 1648 | else |
1649 | dump->flag = fw_dump->tmpl_hdr->drv_cap_mask; | 1649 | dump->flag = fw_dump->cap_mask; |
1650 | 1650 | ||
1651 | dump->version = adapter->fw_version; | 1651 | dump->version = adapter->fw_version; |
1652 | return 0; | 1652 | return 0; |
@@ -1671,9 +1671,10 @@ qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, | |||
1671 | netdev_info(netdev, "Dump not available\n"); | 1671 | netdev_info(netdev, "Dump not available\n"); |
1672 | return -EINVAL; | 1672 | return -EINVAL; |
1673 | } | 1673 | } |
1674 | |||
1674 | /* Copy template header first */ | 1675 | /* Copy template header first */ |
1675 | copy_sz = fw_dump->tmpl_hdr->size; | 1676 | copy_sz = fw_dump->tmpl_hdr_size; |
1676 | hdr_ptr = (u32 *) fw_dump->tmpl_hdr; | 1677 | hdr_ptr = (u32 *)fw_dump->tmpl_hdr; |
1677 | data = buffer; | 1678 | data = buffer; |
1678 | for (i = 0; i < copy_sz/sizeof(u32); i++) | 1679 | for (i = 0; i < copy_sz/sizeof(u32); i++) |
1679 | *data++ = cpu_to_le32(*hdr_ptr++); | 1680 | *data++ = cpu_to_le32(*hdr_ptr++); |
@@ -1681,7 +1682,7 @@ qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, | |||
1681 | /* Copy captured dump data */ | 1682 | /* Copy captured dump data */ |
1682 | memcpy(buffer + copy_sz, fw_dump->data, fw_dump->size); | 1683 | memcpy(buffer + copy_sz, fw_dump->data, fw_dump->size); |
1683 | dump->len = copy_sz + fw_dump->size; | 1684 | dump->len = copy_sz + fw_dump->size; |
1684 | dump->flag = fw_dump->tmpl_hdr->drv_cap_mask; | 1685 | dump->flag = fw_dump->cap_mask; |
1685 | 1686 | ||
1686 | /* Free dump area once data has been captured */ | 1687 | /* Free dump area once data has been captured */ |
1687 | vfree(fw_dump->data); | 1688 | vfree(fw_dump->data); |
@@ -1703,7 +1704,11 @@ static int qlcnic_set_dump_mask(struct qlcnic_adapter *adapter, u32 mask) | |||
1703 | return -EOPNOTSUPP; | 1704 | return -EOPNOTSUPP; |
1704 | } | 1705 | } |
1705 | 1706 | ||
1706 | fw_dump->tmpl_hdr->drv_cap_mask = mask; | 1707 | fw_dump->cap_mask = mask; |
1708 | |||
1709 | /* Store new capture mask in template header as well*/ | ||
1710 | qlcnic_store_cap_mask(adapter, fw_dump->tmpl_hdr, mask); | ||
1711 | |||
1707 | netdev_info(netdev, "Driver mask changed to: 0x%x\n", mask); | 1712 | netdev_info(netdev, "Driver mask changed to: 0x%x\n", mask); |
1708 | return 0; | 1713 | return 0; |
1709 | } | 1714 | } |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h index 63d75617d445..576b301b11ef 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h | |||
@@ -161,6 +161,7 @@ struct qlcnic_host_sds_ring; | |||
161 | struct qlcnic_host_tx_ring; | 161 | struct qlcnic_host_tx_ring; |
162 | struct qlcnic_hardware_context; | 162 | struct qlcnic_hardware_context; |
163 | struct qlcnic_adapter; | 163 | struct qlcnic_adapter; |
164 | struct qlcnic_fw_dump; | ||
164 | 165 | ||
165 | int qlcnic_82xx_hw_read_wx_2M(struct qlcnic_adapter *adapter, ulong, int *); | 166 | int qlcnic_82xx_hw_read_wx_2M(struct qlcnic_adapter *adapter, ulong, int *); |
166 | int qlcnic_82xx_hw_write_wx_2M(struct qlcnic_adapter *, ulong, u32); | 167 | int qlcnic_82xx_hw_write_wx_2M(struct qlcnic_adapter *, ulong, u32); |
@@ -213,4 +214,11 @@ int qlcnic_82xx_shutdown(struct pci_dev *); | |||
213 | int qlcnic_82xx_resume(struct qlcnic_adapter *); | 214 | int qlcnic_82xx_resume(struct qlcnic_adapter *); |
214 | void qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8 failed); | 215 | void qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8 failed); |
215 | void qlcnic_fw_poll_work(struct work_struct *work); | 216 | void qlcnic_fw_poll_work(struct work_struct *work); |
217 | |||
218 | u32 qlcnic_82xx_get_saved_state(void *, u32); | ||
219 | void qlcnic_82xx_set_saved_state(void *, u32, u32); | ||
220 | void qlcnic_82xx_cache_tmpl_hdr_values(struct qlcnic_fw_dump *); | ||
221 | u32 qlcnic_82xx_get_cap_size(void *, int); | ||
222 | void qlcnic_82xx_set_sys_info(void *, int, u32); | ||
223 | void qlcnic_82xx_store_cap_mask(void *, u32); | ||
216 | #endif /* __QLCNIC_HW_H_ */ | 224 | #endif /* __QLCNIC_HW_H_ */ |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index e07fd948d98b..964ba457a7c6 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | |||
@@ -90,7 +90,6 @@ static void qlcnic_82xx_io_resume(struct pci_dev *); | |||
90 | static void qlcnic_82xx_set_mac_filter_count(struct qlcnic_adapter *); | 90 | static void qlcnic_82xx_set_mac_filter_count(struct qlcnic_adapter *); |
91 | static pci_ers_result_t qlcnic_82xx_io_error_detected(struct pci_dev *, | 91 | static pci_ers_result_t qlcnic_82xx_io_error_detected(struct pci_dev *, |
92 | pci_channel_state_t); | 92 | pci_channel_state_t); |
93 | |||
94 | static u32 qlcnic_vlan_tx_check(struct qlcnic_adapter *adapter) | 93 | static u32 qlcnic_vlan_tx_check(struct qlcnic_adapter *adapter) |
95 | { | 94 | { |
96 | struct qlcnic_hardware_context *ahw = adapter->ahw; | 95 | struct qlcnic_hardware_context *ahw = adapter->ahw; |
@@ -561,6 +560,12 @@ static struct qlcnic_hardware_ops qlcnic_hw_ops = { | |||
561 | .disable_sds_intr = qlcnic_82xx_disable_sds_intr, | 560 | .disable_sds_intr = qlcnic_82xx_disable_sds_intr, |
562 | .enable_tx_intr = qlcnic_82xx_enable_tx_intr, | 561 | .enable_tx_intr = qlcnic_82xx_enable_tx_intr, |
563 | .disable_tx_intr = qlcnic_82xx_disable_tx_intr, | 562 | .disable_tx_intr = qlcnic_82xx_disable_tx_intr, |
563 | .get_saved_state = qlcnic_82xx_get_saved_state, | ||
564 | .set_saved_state = qlcnic_82xx_set_saved_state, | ||
565 | .cache_tmpl_hdr_values = qlcnic_82xx_cache_tmpl_hdr_values, | ||
566 | .get_cap_size = qlcnic_82xx_get_cap_size, | ||
567 | .set_sys_info = qlcnic_82xx_set_sys_info, | ||
568 | .store_cap_mask = qlcnic_82xx_store_cap_mask, | ||
564 | }; | 569 | }; |
565 | 570 | ||
566 | static int qlcnic_check_multi_tx_capability(struct qlcnic_adapter *adapter) | 571 | static int qlcnic_check_multi_tx_capability(struct qlcnic_adapter *adapter) |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c index 7763962e2ec4..37b979b1266b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c | |||
@@ -211,6 +211,107 @@ enum qlcnic_minidump_opcode { | |||
211 | QLCNIC_DUMP_RDEND = 255 | 211 | QLCNIC_DUMP_RDEND = 255 |
212 | }; | 212 | }; |
213 | 213 | ||
214 | inline u32 qlcnic_82xx_get_saved_state(void *t_hdr, u32 index) | ||
215 | { | ||
216 | struct qlcnic_82xx_dump_template_hdr *hdr = t_hdr; | ||
217 | |||
218 | return hdr->saved_state[index]; | ||
219 | } | ||
220 | |||
221 | inline void qlcnic_82xx_set_saved_state(void *t_hdr, u32 index, | ||
222 | u32 value) | ||
223 | { | ||
224 | struct qlcnic_82xx_dump_template_hdr *hdr = t_hdr; | ||
225 | |||
226 | hdr->saved_state[index] = value; | ||
227 | } | ||
228 | |||
229 | void qlcnic_82xx_cache_tmpl_hdr_values(struct qlcnic_fw_dump *fw_dump) | ||
230 | { | ||
231 | struct qlcnic_82xx_dump_template_hdr *hdr; | ||
232 | |||
233 | hdr = fw_dump->tmpl_hdr; | ||
234 | fw_dump->tmpl_hdr_size = hdr->size; | ||
235 | fw_dump->version = hdr->version; | ||
236 | fw_dump->num_entries = hdr->num_entries; | ||
237 | fw_dump->offset = hdr->offset; | ||
238 | |||
239 | hdr->drv_cap_mask = hdr->cap_mask; | ||
240 | fw_dump->cap_mask = hdr->cap_mask; | ||
241 | } | ||
242 | |||
243 | inline u32 qlcnic_82xx_get_cap_size(void *t_hdr, int index) | ||
244 | { | ||
245 | struct qlcnic_82xx_dump_template_hdr *hdr = t_hdr; | ||
246 | |||
247 | return hdr->cap_sizes[index]; | ||
248 | } | ||
249 | |||
250 | void qlcnic_82xx_set_sys_info(void *t_hdr, int idx, u32 value) | ||
251 | { | ||
252 | struct qlcnic_82xx_dump_template_hdr *hdr = t_hdr; | ||
253 | |||
254 | hdr->sys_info[idx] = value; | ||
255 | } | ||
256 | |||
257 | void qlcnic_82xx_store_cap_mask(void *tmpl_hdr, u32 mask) | ||
258 | { | ||
259 | struct qlcnic_82xx_dump_template_hdr *hdr = tmpl_hdr; | ||
260 | |||
261 | hdr->drv_cap_mask = mask; | ||
262 | } | ||
263 | |||
264 | inline u32 qlcnic_83xx_get_saved_state(void *t_hdr, u32 index) | ||
265 | { | ||
266 | struct qlcnic_83xx_dump_template_hdr *hdr = t_hdr; | ||
267 | |||
268 | return hdr->saved_state[index]; | ||
269 | } | ||
270 | |||
271 | inline void qlcnic_83xx_set_saved_state(void *t_hdr, u32 index, | ||
272 | u32 value) | ||
273 | { | ||
274 | struct qlcnic_83xx_dump_template_hdr *hdr = t_hdr; | ||
275 | |||
276 | hdr->saved_state[index] = value; | ||
277 | } | ||
278 | |||
279 | void qlcnic_83xx_cache_tmpl_hdr_values(struct qlcnic_fw_dump *fw_dump) | ||
280 | { | ||
281 | struct qlcnic_83xx_dump_template_hdr *hdr; | ||
282 | |||
283 | hdr = fw_dump->tmpl_hdr; | ||
284 | fw_dump->tmpl_hdr_size = hdr->size; | ||
285 | fw_dump->version = hdr->version; | ||
286 | fw_dump->num_entries = hdr->num_entries; | ||
287 | fw_dump->offset = hdr->offset; | ||
288 | |||
289 | hdr->drv_cap_mask = hdr->cap_mask; | ||
290 | fw_dump->cap_mask = hdr->cap_mask; | ||
291 | } | ||
292 | |||
293 | inline u32 qlcnic_83xx_get_cap_size(void *t_hdr, int index) | ||
294 | { | ||
295 | struct qlcnic_83xx_dump_template_hdr *hdr = t_hdr; | ||
296 | |||
297 | return hdr->cap_sizes[index]; | ||
298 | } | ||
299 | |||
300 | void qlcnic_83xx_set_sys_info(void *t_hdr, int idx, u32 value) | ||
301 | { | ||
302 | struct qlcnic_83xx_dump_template_hdr *hdr = t_hdr; | ||
303 | |||
304 | hdr->sys_info[idx] = value; | ||
305 | } | ||
306 | |||
307 | void qlcnic_83xx_store_cap_mask(void *tmpl_hdr, u32 mask) | ||
308 | { | ||
309 | struct qlcnic_83xx_dump_template_hdr *hdr; | ||
310 | |||
311 | hdr = tmpl_hdr; | ||
312 | hdr->drv_cap_mask = mask; | ||
313 | } | ||
314 | |||
214 | struct qlcnic_dump_operations { | 315 | struct qlcnic_dump_operations { |
215 | enum qlcnic_minidump_opcode opcode; | 316 | enum qlcnic_minidump_opcode opcode; |
216 | u32 (*handler)(struct qlcnic_adapter *, struct qlcnic_dump_entry *, | 317 | u32 (*handler)(struct qlcnic_adapter *, struct qlcnic_dump_entry *, |
@@ -238,11 +339,11 @@ static u32 qlcnic_dump_crb(struct qlcnic_adapter *adapter, | |||
238 | static u32 qlcnic_dump_ctrl(struct qlcnic_adapter *adapter, | 339 | static u32 qlcnic_dump_ctrl(struct qlcnic_adapter *adapter, |
239 | struct qlcnic_dump_entry *entry, __le32 *buffer) | 340 | struct qlcnic_dump_entry *entry, __le32 *buffer) |
240 | { | 341 | { |
342 | void *hdr = adapter->ahw->fw_dump.tmpl_hdr; | ||
343 | struct __ctrl *ctr = &entry->region.ctrl; | ||
241 | int i, k, timeout = 0; | 344 | int i, k, timeout = 0; |
242 | u32 addr, data; | 345 | u32 addr, data, temp; |
243 | u8 no_ops; | 346 | u8 no_ops; |
244 | struct __ctrl *ctr = &entry->region.ctrl; | ||
245 | struct qlcnic_dump_template_hdr *t_hdr = adapter->ahw->fw_dump.tmpl_hdr; | ||
246 | 347 | ||
247 | addr = ctr->addr; | 348 | addr = ctr->addr; |
248 | no_ops = ctr->no_ops; | 349 | no_ops = ctr->no_ops; |
@@ -285,29 +386,42 @@ static u32 qlcnic_dump_ctrl(struct qlcnic_adapter *adapter, | |||
285 | } | 386 | } |
286 | break; | 387 | break; |
287 | case QLCNIC_DUMP_RD_SAVE: | 388 | case QLCNIC_DUMP_RD_SAVE: |
288 | if (ctr->index_a) | 389 | temp = ctr->index_a; |
289 | addr = t_hdr->saved_state[ctr->index_a]; | 390 | if (temp) |
391 | addr = qlcnic_get_saved_state(adapter, | ||
392 | hdr, | ||
393 | temp); | ||
290 | data = qlcnic_ind_rd(adapter, addr); | 394 | data = qlcnic_ind_rd(adapter, addr); |
291 | t_hdr->saved_state[ctr->index_v] = data; | 395 | qlcnic_set_saved_state(adapter, hdr, |
396 | ctr->index_v, data); | ||
292 | break; | 397 | break; |
293 | case QLCNIC_DUMP_WRT_SAVED: | 398 | case QLCNIC_DUMP_WRT_SAVED: |
294 | if (ctr->index_v) | 399 | temp = ctr->index_v; |
295 | data = t_hdr->saved_state[ctr->index_v]; | 400 | if (temp) |
401 | data = qlcnic_get_saved_state(adapter, | ||
402 | hdr, | ||
403 | temp); | ||
296 | else | 404 | else |
297 | data = ctr->val1; | 405 | data = ctr->val1; |
298 | if (ctr->index_a) | 406 | |
299 | addr = t_hdr->saved_state[ctr->index_a]; | 407 | temp = ctr->index_a; |
408 | if (temp) | ||
409 | addr = qlcnic_get_saved_state(adapter, | ||
410 | hdr, | ||
411 | temp); | ||
300 | qlcnic_ind_wr(adapter, addr, data); | 412 | qlcnic_ind_wr(adapter, addr, data); |
301 | break; | 413 | break; |
302 | case QLCNIC_DUMP_MOD_SAVE_ST: | 414 | case QLCNIC_DUMP_MOD_SAVE_ST: |
303 | data = t_hdr->saved_state[ctr->index_v]; | 415 | data = qlcnic_get_saved_state(adapter, hdr, |
416 | ctr->index_v); | ||
304 | data <<= ctr->shl_val; | 417 | data <<= ctr->shl_val; |
305 | data >>= ctr->shr_val; | 418 | data >>= ctr->shr_val; |
306 | if (ctr->val2) | 419 | if (ctr->val2) |
307 | data &= ctr->val2; | 420 | data &= ctr->val2; |
308 | data |= ctr->val3; | 421 | data |= ctr->val3; |
309 | data += ctr->val1; | 422 | data += ctr->val1; |
310 | t_hdr->saved_state[ctr->index_v] = data; | 423 | qlcnic_set_saved_state(adapter, hdr, |
424 | ctr->index_v, data); | ||
311 | break; | 425 | break; |
312 | default: | 426 | default: |
313 | dev_info(&adapter->pdev->dev, | 427 | dev_info(&adapter->pdev->dev, |
@@ -544,7 +658,7 @@ out: | |||
544 | static int qlcnic_start_pex_dma(struct qlcnic_adapter *adapter, | 658 | static int qlcnic_start_pex_dma(struct qlcnic_adapter *adapter, |
545 | struct __mem *mem) | 659 | struct __mem *mem) |
546 | { | 660 | { |
547 | struct qlcnic_dump_template_hdr *tmpl_hdr; | 661 | struct qlcnic_83xx_dump_template_hdr *tmpl_hdr; |
548 | struct device *dev = &adapter->pdev->dev; | 662 | struct device *dev = &adapter->pdev->dev; |
549 | u32 dma_no, dma_base_addr, temp_addr; | 663 | u32 dma_no, dma_base_addr, temp_addr; |
550 | int i, ret, dma_sts; | 664 | int i, ret, dma_sts; |
@@ -596,7 +710,7 @@ static u32 qlcnic_read_memory_pexdma(struct qlcnic_adapter *adapter, | |||
596 | struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; | 710 | struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; |
597 | u32 temp, dma_base_addr, size = 0, read_size = 0; | 711 | u32 temp, dma_base_addr, size = 0, read_size = 0; |
598 | struct qlcnic_pex_dma_descriptor *dma_descr; | 712 | struct qlcnic_pex_dma_descriptor *dma_descr; |
599 | struct qlcnic_dump_template_hdr *tmpl_hdr; | 713 | struct qlcnic_83xx_dump_template_hdr *tmpl_hdr; |
600 | struct device *dev = &adapter->pdev->dev; | 714 | struct device *dev = &adapter->pdev->dev; |
601 | dma_addr_t dma_phys_addr; | 715 | dma_addr_t dma_phys_addr; |
602 | void *dma_buffer; | 716 | void *dma_buffer; |
@@ -938,8 +1052,8 @@ static int | |||
938 | qlcnic_fw_flash_get_minidump_temp_size(struct qlcnic_adapter *adapter, | 1052 | qlcnic_fw_flash_get_minidump_temp_size(struct qlcnic_adapter *adapter, |
939 | struct qlcnic_cmd_args *cmd) | 1053 | struct qlcnic_cmd_args *cmd) |
940 | { | 1054 | { |
941 | struct qlcnic_dump_template_hdr tmp_hdr; | 1055 | struct qlcnic_83xx_dump_template_hdr tmp_hdr; |
942 | u32 size = sizeof(struct qlcnic_dump_template_hdr) / sizeof(u32); | 1056 | u32 size = sizeof(tmp_hdr) / sizeof(u32); |
943 | int ret = 0; | 1057 | int ret = 0; |
944 | 1058 | ||
945 | if (qlcnic_82xx_check(adapter)) | 1059 | if (qlcnic_82xx_check(adapter)) |
@@ -1027,17 +1141,19 @@ free_mem: | |||
1027 | return err; | 1141 | return err; |
1028 | } | 1142 | } |
1029 | 1143 | ||
1144 | #define QLCNIC_TEMPLATE_VERSION (0x20001) | ||
1145 | |||
1030 | int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter) | 1146 | int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter) |
1031 | { | 1147 | { |
1032 | int err; | ||
1033 | u32 temp_size = 0; | ||
1034 | u32 version, csum, *tmp_buf; | ||
1035 | struct qlcnic_hardware_context *ahw; | 1148 | struct qlcnic_hardware_context *ahw; |
1036 | struct qlcnic_dump_template_hdr *tmpl_hdr; | 1149 | struct qlcnic_fw_dump *fw_dump; |
1150 | u32 version, csum, *tmp_buf; | ||
1037 | u8 use_flash_temp = 0; | 1151 | u8 use_flash_temp = 0; |
1152 | u32 temp_size = 0; | ||
1153 | int err; | ||
1038 | 1154 | ||
1039 | ahw = adapter->ahw; | 1155 | ahw = adapter->ahw; |
1040 | 1156 | fw_dump = &ahw->fw_dump; | |
1041 | err = qlcnic_fw_get_minidump_temp_size(adapter, &version, &temp_size, | 1157 | err = qlcnic_fw_get_minidump_temp_size(adapter, &version, &temp_size, |
1042 | &use_flash_temp); | 1158 | &use_flash_temp); |
1043 | if (err) { | 1159 | if (err) { |
@@ -1046,11 +1162,11 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter) | |||
1046 | return -EIO; | 1162 | return -EIO; |
1047 | } | 1163 | } |
1048 | 1164 | ||
1049 | ahw->fw_dump.tmpl_hdr = vzalloc(temp_size); | 1165 | fw_dump->tmpl_hdr = vzalloc(temp_size); |
1050 | if (!ahw->fw_dump.tmpl_hdr) | 1166 | if (!fw_dump->tmpl_hdr) |
1051 | return -ENOMEM; | 1167 | return -ENOMEM; |
1052 | 1168 | ||
1053 | tmp_buf = (u32 *)ahw->fw_dump.tmpl_hdr; | 1169 | tmp_buf = (u32 *)fw_dump->tmpl_hdr; |
1054 | if (use_flash_temp) | 1170 | if (use_flash_temp) |
1055 | goto flash_temp; | 1171 | goto flash_temp; |
1056 | 1172 | ||
@@ -1065,8 +1181,8 @@ flash_temp: | |||
1065 | dev_err(&adapter->pdev->dev, | 1181 | dev_err(&adapter->pdev->dev, |
1066 | "Failed to get minidump template header %d\n", | 1182 | "Failed to get minidump template header %d\n", |
1067 | err); | 1183 | err); |
1068 | vfree(ahw->fw_dump.tmpl_hdr); | 1184 | vfree(fw_dump->tmpl_hdr); |
1069 | ahw->fw_dump.tmpl_hdr = NULL; | 1185 | fw_dump->tmpl_hdr = NULL; |
1070 | return -EIO; | 1186 | return -EIO; |
1071 | } | 1187 | } |
1072 | } | 1188 | } |
@@ -1076,21 +1192,22 @@ flash_temp: | |||
1076 | if (csum) { | 1192 | if (csum) { |
1077 | dev_err(&adapter->pdev->dev, | 1193 | dev_err(&adapter->pdev->dev, |
1078 | "Template header checksum validation failed\n"); | 1194 | "Template header checksum validation failed\n"); |
1079 | vfree(ahw->fw_dump.tmpl_hdr); | 1195 | vfree(fw_dump->tmpl_hdr); |
1080 | ahw->fw_dump.tmpl_hdr = NULL; | 1196 | fw_dump->tmpl_hdr = NULL; |
1081 | return -EIO; | 1197 | return -EIO; |
1082 | } | 1198 | } |
1083 | 1199 | ||
1084 | tmpl_hdr = ahw->fw_dump.tmpl_hdr; | 1200 | qlcnic_cache_tmpl_hdr_values(adapter, fw_dump); |
1085 | tmpl_hdr->drv_cap_mask = tmpl_hdr->cap_mask; | 1201 | |
1086 | dev_info(&adapter->pdev->dev, | 1202 | dev_info(&adapter->pdev->dev, |
1087 | "Default minidump capture mask 0x%x\n", | 1203 | "Default minidump capture mask 0x%x\n", |
1088 | tmpl_hdr->cap_mask); | 1204 | fw_dump->cap_mask); |
1089 | 1205 | ||
1090 | if ((tmpl_hdr->version & 0xfffff) >= 0x20001) | 1206 | if (qlcnic_83xx_check(adapter) && |
1091 | ahw->fw_dump.use_pex_dma = true; | 1207 | (fw_dump->version & 0xfffff) >= QLCNIC_TEMPLATE_VERSION) |
1208 | fw_dump->use_pex_dma = true; | ||
1092 | else | 1209 | else |
1093 | ahw->fw_dump.use_pex_dma = false; | 1210 | fw_dump->use_pex_dma = false; |
1094 | 1211 | ||
1095 | qlcnic_enable_fw_dump_state(adapter); | 1212 | qlcnic_enable_fw_dump_state(adapter); |
1096 | 1213 | ||
@@ -1099,21 +1216,22 @@ flash_temp: | |||
1099 | 1216 | ||
1100 | int qlcnic_dump_fw(struct qlcnic_adapter *adapter) | 1217 | int qlcnic_dump_fw(struct qlcnic_adapter *adapter) |
1101 | { | 1218 | { |
1102 | __le32 *buffer; | ||
1103 | u32 ocm_window; | ||
1104 | char mesg[64]; | ||
1105 | char *msg[] = {mesg, NULL}; | ||
1106 | int i, k, ops_cnt, ops_index, dump_size = 0; | ||
1107 | u32 entry_offset, dump, no_entries, buf_offset = 0; | ||
1108 | struct qlcnic_dump_entry *entry; | ||
1109 | struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; | 1219 | struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; |
1110 | struct qlcnic_dump_template_hdr *tmpl_hdr = fw_dump->tmpl_hdr; | ||
1111 | static const struct qlcnic_dump_operations *fw_dump_ops; | 1220 | static const struct qlcnic_dump_operations *fw_dump_ops; |
1221 | struct qlcnic_83xx_dump_template_hdr *hdr_83xx; | ||
1222 | u32 entry_offset, dump, no_entries, buf_offset = 0; | ||
1223 | int i, k, ops_cnt, ops_index, dump_size = 0; | ||
1112 | struct device *dev = &adapter->pdev->dev; | 1224 | struct device *dev = &adapter->pdev->dev; |
1113 | struct qlcnic_hardware_context *ahw; | 1225 | struct qlcnic_hardware_context *ahw; |
1114 | void *temp_buffer; | 1226 | struct qlcnic_dump_entry *entry; |
1227 | void *temp_buffer, *tmpl_hdr; | ||
1228 | u32 ocm_window; | ||
1229 | __le32 *buffer; | ||
1230 | char mesg[64]; | ||
1231 | char *msg[] = {mesg, NULL}; | ||
1115 | 1232 | ||
1116 | ahw = adapter->ahw; | 1233 | ahw = adapter->ahw; |
1234 | tmpl_hdr = fw_dump->tmpl_hdr; | ||
1117 | 1235 | ||
1118 | /* Return if we don't have firmware dump template header */ | 1236 | /* Return if we don't have firmware dump template header */ |
1119 | if (!tmpl_hdr) | 1237 | if (!tmpl_hdr) |
@@ -1133,8 +1251,9 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter) | |||
1133 | netif_info(adapter->ahw, drv, adapter->netdev, "Take FW dump\n"); | 1251 | netif_info(adapter->ahw, drv, adapter->netdev, "Take FW dump\n"); |
1134 | /* Calculate the size for dump data area only */ | 1252 | /* Calculate the size for dump data area only */ |
1135 | for (i = 2, k = 1; (i & QLCNIC_DUMP_MASK_MAX); i <<= 1, k++) | 1253 | for (i = 2, k = 1; (i & QLCNIC_DUMP_MASK_MAX); i <<= 1, k++) |
1136 | if (i & tmpl_hdr->drv_cap_mask) | 1254 | if (i & fw_dump->cap_mask) |
1137 | dump_size += tmpl_hdr->cap_sizes[k]; | 1255 | dump_size += qlcnic_get_cap_size(adapter, tmpl_hdr, k); |
1256 | |||
1138 | if (!dump_size) | 1257 | if (!dump_size) |
1139 | return -EIO; | 1258 | return -EIO; |
1140 | 1259 | ||
@@ -1144,10 +1263,10 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter) | |||
1144 | 1263 | ||
1145 | buffer = fw_dump->data; | 1264 | buffer = fw_dump->data; |
1146 | fw_dump->size = dump_size; | 1265 | fw_dump->size = dump_size; |
1147 | no_entries = tmpl_hdr->num_entries; | 1266 | no_entries = fw_dump->num_entries; |
1148 | entry_offset = tmpl_hdr->offset; | 1267 | entry_offset = fw_dump->offset; |
1149 | tmpl_hdr->sys_info[0] = QLCNIC_DRIVER_VERSION; | 1268 | qlcnic_set_sys_info(adapter, tmpl_hdr, 0, QLCNIC_DRIVER_VERSION); |
1150 | tmpl_hdr->sys_info[1] = adapter->fw_version; | 1269 | qlcnic_set_sys_info(adapter, tmpl_hdr, 1, adapter->fw_version); |
1151 | 1270 | ||
1152 | if (fw_dump->use_pex_dma) { | 1271 | if (fw_dump->use_pex_dma) { |
1153 | temp_buffer = dma_alloc_coherent(dev, QLC_PEX_DMA_READ_SIZE, | 1272 | temp_buffer = dma_alloc_coherent(dev, QLC_PEX_DMA_READ_SIZE, |
@@ -1163,16 +1282,17 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter) | |||
1163 | ops_cnt = ARRAY_SIZE(qlcnic_fw_dump_ops); | 1282 | ops_cnt = ARRAY_SIZE(qlcnic_fw_dump_ops); |
1164 | fw_dump_ops = qlcnic_fw_dump_ops; | 1283 | fw_dump_ops = qlcnic_fw_dump_ops; |
1165 | } else { | 1284 | } else { |
1285 | hdr_83xx = tmpl_hdr; | ||
1166 | ops_cnt = ARRAY_SIZE(qlcnic_83xx_fw_dump_ops); | 1286 | ops_cnt = ARRAY_SIZE(qlcnic_83xx_fw_dump_ops); |
1167 | fw_dump_ops = qlcnic_83xx_fw_dump_ops; | 1287 | fw_dump_ops = qlcnic_83xx_fw_dump_ops; |
1168 | ocm_window = tmpl_hdr->ocm_wnd_reg[adapter->ahw->pci_func]; | 1288 | ocm_window = hdr_83xx->ocm_wnd_reg[ahw->pci_func]; |
1169 | tmpl_hdr->saved_state[QLC_83XX_OCM_INDEX] = ocm_window; | 1289 | hdr_83xx->saved_state[QLC_83XX_OCM_INDEX] = ocm_window; |
1170 | tmpl_hdr->saved_state[QLC_83XX_PCI_INDEX] = ahw->pci_func; | 1290 | hdr_83xx->saved_state[QLC_83XX_PCI_INDEX] = ahw->pci_func; |
1171 | } | 1291 | } |
1172 | 1292 | ||
1173 | for (i = 0; i < no_entries; i++) { | 1293 | for (i = 0; i < no_entries; i++) { |
1174 | entry = (void *)tmpl_hdr + entry_offset; | 1294 | entry = tmpl_hdr + entry_offset; |
1175 | if (!(entry->hdr.mask & tmpl_hdr->drv_cap_mask)) { | 1295 | if (!(entry->hdr.mask & fw_dump->cap_mask)) { |
1176 | entry->hdr.flags |= QLCNIC_DUMP_SKIP; | 1296 | entry->hdr.flags |= QLCNIC_DUMP_SKIP; |
1177 | entry_offset += entry->hdr.offset; | 1297 | entry_offset += entry->hdr.offset; |
1178 | continue; | 1298 | continue; |
@@ -1209,8 +1329,9 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter) | |||
1209 | 1329 | ||
1210 | fw_dump->clr = 1; | 1330 | fw_dump->clr = 1; |
1211 | snprintf(mesg, sizeof(mesg), "FW_DUMP=%s", adapter->netdev->name); | 1331 | snprintf(mesg, sizeof(mesg), "FW_DUMP=%s", adapter->netdev->name); |
1212 | dev_info(dev, "%s: Dump data %d bytes captured, template header size %d bytes\n", | 1332 | netdev_info(adapter->netdev, |
1213 | adapter->netdev->name, fw_dump->size, tmpl_hdr->size); | 1333 | "Dump data %d bytes captured, template header size %d bytes\n", |
1334 | fw_dump->size, fw_dump->tmpl_hdr_size); | ||
1214 | /* Send a udev event to notify availability of FW dump */ | 1335 | /* Send a udev event to notify availability of FW dump */ |
1215 | kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, msg); | 1336 | kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, msg); |
1216 | 1337 | ||