diff options
Diffstat (limited to 'drivers/net/ethernet/qlogic')
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 14 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h | 111 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | 951 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c | 118 |
7 files changed, 1191 insertions, 8 deletions
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 002f0bdc5b5e..78d1db686bdf 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | |||
@@ -1360,6 +1360,7 @@ struct _cdrp_cmd { | |||
1360 | struct qlcnic_cmd_args { | 1360 | struct qlcnic_cmd_args { |
1361 | struct _cdrp_cmd req; | 1361 | struct _cdrp_cmd req; |
1362 | struct _cdrp_cmd rsp; | 1362 | struct _cdrp_cmd rsp; |
1363 | int op_type; | ||
1363 | }; | 1364 | }; |
1364 | 1365 | ||
1365 | int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter); | 1366 | int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter); |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index c08974fd07e3..89dcea593646 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | |||
@@ -14,6 +14,7 @@ | |||
14 | 14 | ||
15 | #define QLCNIC_MAX_TX_QUEUES 1 | 15 | #define QLCNIC_MAX_TX_QUEUES 1 |
16 | #define RSS_HASHTYPE_IP_TCP 0x3 | 16 | #define RSS_HASHTYPE_IP_TCP 0x3 |
17 | #define QLC_83XX_FW_MBX_CMD 0 | ||
17 | 18 | ||
18 | /* status descriptor mailbox data | 19 | /* status descriptor mailbox data |
19 | * @phy_addr_{low|high}: physical address of buffer | 20 | * @phy_addr_{low|high}: physical address of buffer |
@@ -211,6 +212,7 @@ static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = { | |||
211 | {QLCNIC_CMD_GET_LED_CONFIG, 1, 5}, | 212 | {QLCNIC_CMD_GET_LED_CONFIG, 1, 5}, |
212 | {QLCNIC_CMD_ADD_RCV_RINGS, 130, 26}, | 213 | {QLCNIC_CMD_ADD_RCV_RINGS, 130, 26}, |
213 | {QLCNIC_CMD_CONFIG_VPORT, 4, 4}, | 214 | {QLCNIC_CMD_CONFIG_VPORT, 4, 4}, |
215 | {QLCNIC_CMD_BC_EVENT_SETUP, 2, 1}, | ||
214 | }; | 216 | }; |
215 | 217 | ||
216 | const u32 qlcnic_83xx_ext_reg_tbl[] = { | 218 | const u32 qlcnic_83xx_ext_reg_tbl[] = { |
@@ -824,7 +826,7 @@ static void qlcnic_dump_mbx(struct qlcnic_adapter *adapter, | |||
824 | } | 826 | } |
825 | 827 | ||
826 | /* Mailbox response for mac rcode */ | 828 | /* Mailbox response for mac rcode */ |
827 | static u32 qlcnic_83xx_mac_rcode(struct qlcnic_adapter *adapter) | 829 | u32 qlcnic_83xx_mac_rcode(struct qlcnic_adapter *adapter) |
828 | { | 830 | { |
829 | u32 fw_data; | 831 | u32 fw_data; |
830 | u8 mac_cmd_rcode; | 832 | u8 mac_cmd_rcode; |
@@ -838,7 +840,7 @@ static u32 qlcnic_83xx_mac_rcode(struct qlcnic_adapter *adapter) | |||
838 | return 1; | 840 | return 1; |
839 | } | 841 | } |
840 | 842 | ||
841 | static u32 qlcnic_83xx_mbx_poll(struct qlcnic_adapter *adapter) | 843 | u32 qlcnic_83xx_mbx_poll(struct qlcnic_adapter *adapter) |
842 | { | 844 | { |
843 | u32 data; | 845 | u32 data; |
844 | unsigned long wait_time = 0; | 846 | unsigned long wait_time = 0; |
@@ -953,6 +955,7 @@ int qlcnic_83xx_alloc_mbx_args(struct qlcnic_cmd_args *mbx, | |||
953 | size = ARRAY_SIZE(qlcnic_83xx_mbx_tbl); | 955 | size = ARRAY_SIZE(qlcnic_83xx_mbx_tbl); |
954 | for (i = 0; i < size; i++) { | 956 | for (i = 0; i < size; i++) { |
955 | if (type == mbx_tbl[i].cmd) { | 957 | if (type == mbx_tbl[i].cmd) { |
958 | mbx->op_type = QLC_83XX_FW_MBX_CMD; | ||
956 | mbx->req.num = mbx_tbl[i].in_args; | 959 | mbx->req.num = mbx_tbl[i].in_args; |
957 | mbx->rsp.num = mbx_tbl[i].out_args; | 960 | mbx->rsp.num = mbx_tbl[i].out_args; |
958 | mbx->req.arg = kcalloc(mbx->req.num, sizeof(u32), | 961 | mbx->req.arg = kcalloc(mbx->req.num, sizeof(u32), |
@@ -970,10 +973,10 @@ int qlcnic_83xx_alloc_mbx_args(struct qlcnic_cmd_args *mbx, | |||
970 | memset(mbx->rsp.arg, 0, sizeof(u32) * mbx->rsp.num); | 973 | memset(mbx->rsp.arg, 0, sizeof(u32) * mbx->rsp.num); |
971 | temp = adapter->ahw->fw_hal_version << 29; | 974 | temp = adapter->ahw->fw_hal_version << 29; |
972 | mbx->req.arg[0] = (type | (mbx->req.num << 16) | temp); | 975 | mbx->req.arg[0] = (type | (mbx->req.num << 16) | temp); |
973 | break; | 976 | return 0; |
974 | } | 977 | } |
975 | } | 978 | } |
976 | return 0; | 979 | return -EINVAL; |
977 | } | 980 | } |
978 | 981 | ||
979 | void qlcnic_83xx_idc_aen_work(struct work_struct *work) | 982 | void qlcnic_83xx_idc_aen_work(struct work_struct *work) |
@@ -1029,6 +1032,9 @@ void qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter) | |||
1029 | break; | 1032 | break; |
1030 | case QLCNIC_MBX_TIME_EXTEND_EVENT: | 1033 | case QLCNIC_MBX_TIME_EXTEND_EVENT: |
1031 | break; | 1034 | break; |
1035 | case QLCNIC_MBX_BC_EVENT: | ||
1036 | qlcnic_sriov_handle_bc_event(adapter, event[1]); | ||
1037 | break; | ||
1032 | case QLCNIC_MBX_SFP_INSERT_EVENT: | 1038 | case QLCNIC_MBX_SFP_INSERT_EVENT: |
1033 | dev_info(&adapter->pdev->dev, "SFP+ Insert AEN:0x%x.\n", | 1039 | dev_info(&adapter->pdev->dev, "SFP+ Insert AEN:0x%x.\n", |
1034 | QLCNIC_MBX_RSP(event[0])); | 1040 | QLCNIC_MBX_RSP(event[0])); |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h index da0fcc78407b..3e8dc0b136e6 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h | |||
@@ -456,4 +456,6 @@ int qlcnic_83xx_set_led(struct net_device *, enum ethtool_phys_id_state); | |||
456 | int qlcnic_83xx_flash_test(struct qlcnic_adapter *); | 456 | int qlcnic_83xx_flash_test(struct qlcnic_adapter *); |
457 | int qlcnic_83xx_enable_flash_write(struct qlcnic_adapter *); | 457 | int qlcnic_83xx_enable_flash_write(struct qlcnic_adapter *); |
458 | int qlcnic_83xx_disable_flash_write(struct qlcnic_adapter *); | 458 | int qlcnic_83xx_disable_flash_write(struct qlcnic_adapter *); |
459 | u32 qlcnic_83xx_mac_rcode(struct qlcnic_adapter *); | ||
460 | u32 qlcnic_83xx_mbx_poll(struct qlcnic_adapter *); | ||
459 | #endif | 461 | #endif |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h index 91db4ed0a58e..d606e50d8902 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h | |||
@@ -83,6 +83,7 @@ enum qlcnic_regs { | |||
83 | #define QLCNIC_CMD_CONFIG_PORT 0x2e | 83 | #define QLCNIC_CMD_CONFIG_PORT 0x2e |
84 | #define QLCNIC_CMD_TEMP_SIZE 0x2f | 84 | #define QLCNIC_CMD_TEMP_SIZE 0x2f |
85 | #define QLCNIC_CMD_GET_TEMP_HDR 0x30 | 85 | #define QLCNIC_CMD_GET_TEMP_HDR 0x30 |
86 | #define QLCNIC_CMD_BC_EVENT_SETUP 0x31 | ||
86 | #define QLCNIC_CMD_CONFIG_VPORT 0x32 | 87 | #define QLCNIC_CMD_CONFIG_VPORT 0x32 |
87 | #define QLCNIC_CMD_GET_MAC_STATS 0x37 | 88 | #define QLCNIC_CMD_GET_MAC_STATS 0x37 |
88 | #define QLCNIC_CMD_SET_DRV_VER 0x38 | 89 | #define QLCNIC_CMD_SET_DRV_VER 0x38 |
@@ -115,6 +116,7 @@ enum qlcnic_regs { | |||
115 | #define QLCNIC_SET_FAC_DEF_MAC 5 | 116 | #define QLCNIC_SET_FAC_DEF_MAC 5 |
116 | 117 | ||
117 | #define QLCNIC_MBX_LINK_EVENT 0x8001 | 118 | #define QLCNIC_MBX_LINK_EVENT 0x8001 |
119 | #define QLCNIC_MBX_BC_EVENT 0x8002 | ||
118 | #define QLCNIC_MBX_COMP_EVENT 0x8100 | 120 | #define QLCNIC_MBX_COMP_EVENT 0x8100 |
119 | #define QLCNIC_MBX_REQUEST_EVENT 0x8101 | 121 | #define QLCNIC_MBX_REQUEST_EVENT 0x8101 |
120 | #define QLCNIC_MBX_TIME_EXTEND_EVENT 0x8102 | 122 | #define QLCNIC_MBX_TIME_EXTEND_EVENT 0x8102 |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h index e849939bbe5e..00053ad8bc3c 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h | |||
@@ -15,6 +15,84 @@ | |||
15 | extern const u32 qlcnic_83xx_reg_tbl[]; | 15 | extern const u32 qlcnic_83xx_reg_tbl[]; |
16 | extern const u32 qlcnic_83xx_ext_reg_tbl[]; | 16 | extern const u32 qlcnic_83xx_ext_reg_tbl[]; |
17 | 17 | ||
18 | struct qlcnic_bc_payload { | ||
19 | u64 payload[126]; | ||
20 | }; | ||
21 | |||
22 | struct qlcnic_bc_hdr { | ||
23 | #if defined(__LITTLE_ENDIAN) | ||
24 | u8 version; | ||
25 | u8 msg_type:4; | ||
26 | u8 rsvd1:3; | ||
27 | u8 op_type:1; | ||
28 | u8 num_cmds; | ||
29 | u8 num_frags; | ||
30 | u8 frag_num; | ||
31 | u8 cmd_op; | ||
32 | u16 seq_id; | ||
33 | u64 rsvd3; | ||
34 | #elif defined(__BIG_ENDIAN) | ||
35 | u8 num_frags; | ||
36 | u8 num_cmds; | ||
37 | u8 op_type:1; | ||
38 | u8 rsvd1:3; | ||
39 | u8 msg_type:4; | ||
40 | u8 version; | ||
41 | u16 seq_id; | ||
42 | u8 cmd_op; | ||
43 | u8 frag_num; | ||
44 | u64 rsvd3; | ||
45 | #endif | ||
46 | }; | ||
47 | |||
48 | enum qlcnic_bc_commands { | ||
49 | QLCNIC_BC_CMD_CHANNEL_INIT = 0x0, | ||
50 | QLCNIC_BC_CMD_CHANNEL_TERM = 0x1, | ||
51 | }; | ||
52 | |||
53 | #define QLC_BC_CMD 1 | ||
54 | |||
55 | struct qlcnic_trans_list { | ||
56 | /* Lock for manipulating list */ | ||
57 | spinlock_t lock; | ||
58 | struct list_head wait_list; | ||
59 | int count; | ||
60 | }; | ||
61 | |||
62 | enum qlcnic_trans_state { | ||
63 | QLC_INIT = 0, | ||
64 | QLC_WAIT_FOR_CHANNEL_FREE, | ||
65 | QLC_WAIT_FOR_RESP, | ||
66 | QLC_ABORT, | ||
67 | QLC_END, | ||
68 | }; | ||
69 | |||
70 | struct qlcnic_bc_trans { | ||
71 | u8 func_id; | ||
72 | u8 active; | ||
73 | u8 curr_rsp_frag; | ||
74 | u8 curr_req_frag; | ||
75 | u16 cmd_id; | ||
76 | u16 req_pay_size; | ||
77 | u16 rsp_pay_size; | ||
78 | u32 trans_id; | ||
79 | enum qlcnic_trans_state trans_state; | ||
80 | struct list_head list; | ||
81 | struct qlcnic_bc_hdr *req_hdr; | ||
82 | struct qlcnic_bc_hdr *rsp_hdr; | ||
83 | struct qlcnic_bc_payload *req_pay; | ||
84 | struct qlcnic_bc_payload *rsp_pay; | ||
85 | struct completion resp_cmpl; | ||
86 | struct qlcnic_vf_info *vf; | ||
87 | }; | ||
88 | |||
89 | enum qlcnic_vf_state { | ||
90 | QLC_BC_VF_SEND = 0, | ||
91 | QLC_BC_VF_RECV, | ||
92 | QLC_BC_VF_CHANNEL, | ||
93 | QLC_BC_VF_STATE, | ||
94 | }; | ||
95 | |||
18 | struct qlcnic_resources { | 96 | struct qlcnic_resources { |
19 | u16 num_tx_mac_filters; | 97 | u16 num_tx_mac_filters; |
20 | u16 num_rx_ucast_mac_filters; | 98 | u16 num_rx_ucast_mac_filters; |
@@ -34,10 +112,36 @@ struct qlcnic_resources { | |||
34 | u16 max_remote_ipv6_addrs; | 112 | u16 max_remote_ipv6_addrs; |
35 | }; | 113 | }; |
36 | 114 | ||
115 | struct qlcnic_vport { | ||
116 | u16 handle; | ||
117 | u8 mac[6]; | ||
118 | }; | ||
119 | |||
120 | struct qlcnic_vf_info { | ||
121 | u8 pci_func; | ||
122 | unsigned long state; | ||
123 | struct completion ch_free_cmpl; | ||
124 | struct work_struct trans_work; | ||
125 | /* It synchronizes commands sent from VF */ | ||
126 | struct mutex send_cmd_lock; | ||
127 | struct qlcnic_bc_trans *send_cmd; | ||
128 | struct qlcnic_trans_list rcv_act; | ||
129 | struct qlcnic_trans_list rcv_pend; | ||
130 | struct qlcnic_adapter *adapter; | ||
131 | struct qlcnic_vport *vp; | ||
132 | }; | ||
133 | |||
134 | struct qlcnic_back_channel { | ||
135 | u16 trans_counter; | ||
136 | struct workqueue_struct *bc_trans_wq; | ||
137 | }; | ||
138 | |||
37 | struct qlcnic_sriov { | 139 | struct qlcnic_sriov { |
38 | u16 vp_handle; | 140 | u16 vp_handle; |
39 | u8 num_vfs; | 141 | u8 num_vfs; |
40 | struct qlcnic_resources ff_max; | 142 | struct qlcnic_resources ff_max; |
143 | struct qlcnic_back_channel bc; | ||
144 | struct qlcnic_vf_info *vf_info; | ||
41 | }; | 145 | }; |
42 | 146 | ||
43 | int qlcnic_sriov_init(struct qlcnic_adapter *, int); | 147 | int qlcnic_sriov_init(struct qlcnic_adapter *, int); |
@@ -46,6 +150,10 @@ void __qlcnic_sriov_cleanup(struct qlcnic_adapter *); | |||
46 | void qlcnic_sriov_vf_register_map(struct qlcnic_hardware_context *); | 150 | void qlcnic_sriov_vf_register_map(struct qlcnic_hardware_context *); |
47 | int qlcnic_sriov_vf_init(struct qlcnic_adapter *, int); | 151 | int qlcnic_sriov_vf_init(struct qlcnic_adapter *, int); |
48 | void qlcnic_sriov_vf_set_ops(struct qlcnic_adapter *); | 152 | void qlcnic_sriov_vf_set_ops(struct qlcnic_adapter *); |
153 | int qlcnic_sriov_func_to_index(struct qlcnic_adapter *, u8); | ||
154 | int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *, u8); | ||
155 | void qlcnic_sriov_handle_bc_event(struct qlcnic_adapter *, u32); | ||
156 | int qlcnic_sriov_cfg_bc_intr(struct qlcnic_adapter *, u8); | ||
49 | 157 | ||
50 | static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter) | 158 | static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter) |
51 | { | 159 | { |
@@ -53,6 +161,9 @@ static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter) | |||
53 | } | 161 | } |
54 | 162 | ||
55 | #ifdef CONFIG_QLCNIC_SRIOV | 163 | #ifdef CONFIG_QLCNIC_SRIOV |
164 | void qlcnic_sriov_pf_process_bc_cmd(struct qlcnic_adapter *, | ||
165 | struct qlcnic_bc_trans *, | ||
166 | struct qlcnic_cmd_args *); | ||
56 | void qlcnic_sriov_pf_disable(struct qlcnic_adapter *); | 167 | void qlcnic_sriov_pf_disable(struct qlcnic_adapter *); |
57 | void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *); | 168 | void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *); |
58 | int qlcnic_pci_sriov_configure(struct pci_dev *, int); | 169 | int qlcnic_pci_sriov_configure(struct pci_dev *, int); |
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index 0e097f79a14a..0615086f4526 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | |||
@@ -10,6 +10,20 @@ | |||
10 | #include "qlcnic_83xx_hw.h" | 10 | #include "qlcnic_83xx_hw.h" |
11 | #include <linux/types.h> | 11 | #include <linux/types.h> |
12 | 12 | ||
13 | #define QLC_BC_COMMAND 0 | ||
14 | #define QLC_BC_RESPONSE 1 | ||
15 | |||
16 | #define QLC_MBOX_RESP_TIMEOUT (10 * HZ) | ||
17 | #define QLC_MBOX_CH_FREE_TIMEOUT (10 * HZ) | ||
18 | |||
19 | #define QLC_BC_MSG 0 | ||
20 | #define QLC_BC_CFREE 1 | ||
21 | #define QLC_BC_HDR_SZ 16 | ||
22 | #define QLC_BC_PAYLOAD_SZ (1024 - QLC_BC_HDR_SZ) | ||
23 | |||
24 | static int qlcnic_sriov_vf_mbx_op(struct qlcnic_adapter *, | ||
25 | struct qlcnic_cmd_args *); | ||
26 | |||
13 | static struct qlcnic_hardware_ops qlcnic_sriov_vf_hw_ops = { | 27 | static struct qlcnic_hardware_ops qlcnic_sriov_vf_hw_ops = { |
14 | .read_crb = qlcnic_83xx_read_crb, | 28 | .read_crb = qlcnic_83xx_read_crb, |
15 | .write_crb = qlcnic_83xx_write_crb, | 29 | .write_crb = qlcnic_83xx_write_crb, |
@@ -18,6 +32,7 @@ static struct qlcnic_hardware_ops qlcnic_sriov_vf_hw_ops = { | |||
18 | .get_mac_address = qlcnic_83xx_get_mac_address, | 32 | .get_mac_address = qlcnic_83xx_get_mac_address, |
19 | .setup_intr = qlcnic_83xx_setup_intr, | 33 | .setup_intr = qlcnic_83xx_setup_intr, |
20 | .alloc_mbx_args = qlcnic_83xx_alloc_mbx_args, | 34 | .alloc_mbx_args = qlcnic_83xx_alloc_mbx_args, |
35 | .mbx_cmd = qlcnic_sriov_vf_mbx_op, | ||
21 | .get_func_no = qlcnic_83xx_get_func_no, | 36 | .get_func_no = qlcnic_83xx_get_func_no, |
22 | .api_lock = qlcnic_83xx_cam_lock, | 37 | .api_lock = qlcnic_83xx_cam_lock, |
23 | .api_unlock = qlcnic_83xx_cam_unlock, | 38 | .api_unlock = qlcnic_83xx_cam_unlock, |
@@ -49,9 +64,50 @@ static struct qlcnic_nic_template qlcnic_sriov_vf_ops = { | |||
49 | .clear_legacy_intr = qlcnic_83xx_clear_legacy_intr, | 64 | .clear_legacy_intr = qlcnic_83xx_clear_legacy_intr, |
50 | }; | 65 | }; |
51 | 66 | ||
67 | static const struct qlcnic_mailbox_metadata qlcnic_sriov_bc_mbx_tbl[] = { | ||
68 | {QLCNIC_BC_CMD_CHANNEL_INIT, 2, 2}, | ||
69 | {QLCNIC_BC_CMD_CHANNEL_TERM, 2, 2}, | ||
70 | }; | ||
71 | |||
72 | static inline bool qlcnic_sriov_bc_msg_check(u32 val) | ||
73 | { | ||
74 | return (val & (1 << QLC_BC_MSG)) ? true : false; | ||
75 | } | ||
76 | |||
77 | static inline bool qlcnic_sriov_channel_free_check(u32 val) | ||
78 | { | ||
79 | return (val & (1 << QLC_BC_CFREE)) ? true : false; | ||
80 | } | ||
81 | |||
82 | static inline u8 qlcnic_sriov_target_func_id(u32 val) | ||
83 | { | ||
84 | return (val >> 4) & 0xff; | ||
85 | } | ||
86 | |||
87 | static int qlcnic_sriov_virtid_fn(struct qlcnic_adapter *adapter, int vf_id) | ||
88 | { | ||
89 | struct pci_dev *dev = adapter->pdev; | ||
90 | int pos; | ||
91 | u16 stride, offset; | ||
92 | |||
93 | if (qlcnic_sriov_vf_check(adapter)) | ||
94 | return 0; | ||
95 | |||
96 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); | ||
97 | pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset); | ||
98 | pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride); | ||
99 | |||
100 | return (dev->devfn + offset + stride * vf_id) & 0xff; | ||
101 | } | ||
102 | |||
52 | int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs) | 103 | int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs) |
53 | { | 104 | { |
54 | struct qlcnic_sriov *sriov; | 105 | struct qlcnic_sriov *sriov; |
106 | struct qlcnic_back_channel *bc; | ||
107 | struct workqueue_struct *wq; | ||
108 | struct qlcnic_vport *vp; | ||
109 | struct qlcnic_vf_info *vf; | ||
110 | int err, i; | ||
55 | 111 | ||
56 | if (!qlcnic_sriov_enable_check(adapter)) | 112 | if (!qlcnic_sriov_enable_check(adapter)) |
57 | return -EIO; | 113 | return -EIO; |
@@ -62,19 +118,84 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs) | |||
62 | 118 | ||
63 | adapter->ahw->sriov = sriov; | 119 | adapter->ahw->sriov = sriov; |
64 | sriov->num_vfs = num_vfs; | 120 | sriov->num_vfs = num_vfs; |
121 | bc = &sriov->bc; | ||
122 | sriov->vf_info = kzalloc(sizeof(struct qlcnic_vf_info) * | ||
123 | num_vfs, GFP_KERNEL); | ||
124 | if (!sriov->vf_info) { | ||
125 | err = -ENOMEM; | ||
126 | goto qlcnic_free_sriov; | ||
127 | } | ||
128 | |||
129 | wq = create_singlethread_workqueue("bc-trans"); | ||
130 | if (wq == NULL) { | ||
131 | err = -ENOMEM; | ||
132 | dev_err(&adapter->pdev->dev, | ||
133 | "Cannot create bc-trans workqueue\n"); | ||
134 | goto qlcnic_free_vf_info; | ||
135 | } | ||
136 | |||
137 | bc->bc_trans_wq = wq; | ||
138 | |||
139 | for (i = 0; i < num_vfs; i++) { | ||
140 | vf = &sriov->vf_info[i]; | ||
141 | vf->adapter = adapter; | ||
142 | vf->pci_func = qlcnic_sriov_virtid_fn(adapter, i); | ||
143 | mutex_init(&vf->send_cmd_lock); | ||
144 | INIT_LIST_HEAD(&vf->rcv_act.wait_list); | ||
145 | INIT_LIST_HEAD(&vf->rcv_pend.wait_list); | ||
146 | spin_lock_init(&vf->rcv_act.lock); | ||
147 | spin_lock_init(&vf->rcv_pend.lock); | ||
148 | init_completion(&vf->ch_free_cmpl); | ||
149 | |||
150 | if (qlcnic_sriov_pf_check(adapter)) { | ||
151 | vp = kzalloc(sizeof(struct qlcnic_vport), GFP_KERNEL); | ||
152 | if (!vp) { | ||
153 | err = -ENOMEM; | ||
154 | goto qlcnic_destroy_trans_wq; | ||
155 | } | ||
156 | sriov->vf_info[i].vp = vp; | ||
157 | random_ether_addr(vp->mac); | ||
158 | dev_info(&adapter->pdev->dev, | ||
159 | "MAC Address %pM is configured for VF %d\n", | ||
160 | vp->mac, i); | ||
161 | } | ||
162 | } | ||
163 | |||
65 | return 0; | 164 | return 0; |
165 | |||
166 | qlcnic_destroy_trans_wq: | ||
167 | destroy_workqueue(bc->bc_trans_wq); | ||
168 | |||
169 | qlcnic_free_vf_info: | ||
170 | kfree(sriov->vf_info); | ||
171 | |||
172 | qlcnic_free_sriov: | ||
173 | kfree(adapter->ahw->sriov); | ||
174 | return err; | ||
66 | } | 175 | } |
67 | 176 | ||
68 | void __qlcnic_sriov_cleanup(struct qlcnic_adapter *adapter) | 177 | void __qlcnic_sriov_cleanup(struct qlcnic_adapter *adapter) |
69 | { | 178 | { |
179 | struct qlcnic_sriov *sriov = adapter->ahw->sriov; | ||
180 | struct qlcnic_back_channel *bc = &sriov->bc; | ||
181 | int i; | ||
182 | |||
70 | if (!qlcnic_sriov_enable_check(adapter)) | 183 | if (!qlcnic_sriov_enable_check(adapter)) |
71 | return; | 184 | return; |
72 | 185 | ||
186 | destroy_workqueue(bc->bc_trans_wq); | ||
187 | |||
188 | for (i = 0; i < sriov->num_vfs; i++) | ||
189 | kfree(sriov->vf_info[i].vp); | ||
190 | |||
191 | kfree(sriov->vf_info); | ||
73 | kfree(adapter->ahw->sriov); | 192 | kfree(adapter->ahw->sriov); |
74 | } | 193 | } |
75 | 194 | ||
76 | static void qlcnic_sriov_vf_cleanup(struct qlcnic_adapter *adapter) | 195 | static void qlcnic_sriov_vf_cleanup(struct qlcnic_adapter *adapter) |
77 | { | 196 | { |
197 | qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_TERM); | ||
198 | qlcnic_sriov_cfg_bc_intr(adapter, 0); | ||
78 | __qlcnic_sriov_cleanup(adapter); | 199 | __qlcnic_sriov_cleanup(adapter); |
79 | } | 200 | } |
80 | 201 | ||
@@ -87,6 +208,103 @@ void qlcnic_sriov_cleanup(struct qlcnic_adapter *adapter) | |||
87 | qlcnic_sriov_vf_cleanup(adapter); | 208 | qlcnic_sriov_vf_cleanup(adapter); |
88 | } | 209 | } |
89 | 210 | ||
211 | static int qlcnic_sriov_post_bc_msg(struct qlcnic_adapter *adapter, u32 *hdr, | ||
212 | u32 *pay, u8 pci_func, u8 size) | ||
213 | { | ||
214 | struct qlcnic_hardware_context *ahw = adapter->ahw; | ||
215 | unsigned long flags; | ||
216 | u32 rsp, mbx_val, fw_data, rsp_num, mbx_cmd, val; | ||
217 | u16 opcode; | ||
218 | u8 mbx_err_code; | ||
219 | int i, j; | ||
220 | |||
221 | opcode = ((struct qlcnic_bc_hdr *)hdr)->cmd_op; | ||
222 | |||
223 | if (!test_bit(QLC_83XX_MBX_READY, &adapter->ahw->idc.status)) { | ||
224 | dev_info(&adapter->pdev->dev, | ||
225 | "Mailbox cmd attempted, 0x%x\n", opcode); | ||
226 | dev_info(&adapter->pdev->dev, "Mailbox detached\n"); | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | spin_lock_irqsave(&ahw->mbx_lock, flags); | ||
231 | |||
232 | mbx_val = QLCRDX(ahw, QLCNIC_HOST_MBX_CTRL); | ||
233 | if (mbx_val) { | ||
234 | QLCDB(adapter, DRV, "Mailbox cmd attempted, 0x%x\n", opcode); | ||
235 | spin_unlock_irqrestore(&ahw->mbx_lock, flags); | ||
236 | return QLCNIC_RCODE_TIMEOUT; | ||
237 | } | ||
238 | /* Fill in mailbox registers */ | ||
239 | val = size + (sizeof(struct qlcnic_bc_hdr) / sizeof(u32)); | ||
240 | mbx_cmd = 0x31 | (val << 16) | (adapter->ahw->fw_hal_version << 29); | ||
241 | |||
242 | writel(mbx_cmd, QLCNIC_MBX_HOST(ahw, 0)); | ||
243 | mbx_cmd = 0x1 | (1 << 4); | ||
244 | |||
245 | if (qlcnic_sriov_pf_check(adapter)) | ||
246 | mbx_cmd |= (pci_func << 5); | ||
247 | |||
248 | writel(mbx_cmd, QLCNIC_MBX_HOST(ahw, 1)); | ||
249 | for (i = 2, j = 0; j < (sizeof(struct qlcnic_bc_hdr) / sizeof(u32)); | ||
250 | i++, j++) { | ||
251 | writel(*(hdr++), QLCNIC_MBX_HOST(ahw, i)); | ||
252 | } | ||
253 | for (j = 0; j < size; j++, i++) | ||
254 | writel(*(pay++), QLCNIC_MBX_HOST(ahw, i)); | ||
255 | |||
256 | /* Signal FW about the impending command */ | ||
257 | QLCWRX(ahw, QLCNIC_HOST_MBX_CTRL, QLCNIC_SET_OWNER); | ||
258 | |||
259 | /* Waiting for the mailbox cmd to complete and while waiting here | ||
260 | * some AEN might arrive. If more than 5 seconds expire we can | ||
261 | * assume something is wrong. | ||
262 | */ | ||
263 | poll: | ||
264 | rsp = qlcnic_83xx_mbx_poll(adapter); | ||
265 | if (rsp != QLCNIC_RCODE_TIMEOUT) { | ||
266 | /* Get the FW response data */ | ||
267 | fw_data = readl(QLCNIC_MBX_FW(ahw, 0)); | ||
268 | if (fw_data & QLCNIC_MBX_ASYNC_EVENT) { | ||
269 | qlcnic_83xx_process_aen(adapter); | ||
270 | mbx_val = QLCRDX(ahw, QLCNIC_HOST_MBX_CTRL); | ||
271 | if (mbx_val) | ||
272 | goto poll; | ||
273 | } | ||
274 | mbx_err_code = QLCNIC_MBX_STATUS(fw_data); | ||
275 | rsp_num = QLCNIC_MBX_NUM_REGS(fw_data); | ||
276 | opcode = QLCNIC_MBX_RSP(fw_data); | ||
277 | |||
278 | switch (mbx_err_code) { | ||
279 | case QLCNIC_MBX_RSP_OK: | ||
280 | case QLCNIC_MBX_PORT_RSP_OK: | ||
281 | rsp = QLCNIC_RCODE_SUCCESS; | ||
282 | break; | ||
283 | default: | ||
284 | if (opcode == QLCNIC_CMD_CONFIG_MAC_VLAN) { | ||
285 | rsp = qlcnic_83xx_mac_rcode(adapter); | ||
286 | if (!rsp) | ||
287 | goto out; | ||
288 | } | ||
289 | dev_err(&adapter->pdev->dev, | ||
290 | "MBX command 0x%x failed with err:0x%x\n", | ||
291 | opcode, mbx_err_code); | ||
292 | rsp = mbx_err_code; | ||
293 | break; | ||
294 | } | ||
295 | goto out; | ||
296 | } | ||
297 | |||
298 | dev_err(&adapter->pdev->dev, "MBX command 0x%x timed out\n", | ||
299 | QLCNIC_MBX_RSP(mbx_cmd)); | ||
300 | rsp = QLCNIC_RCODE_TIMEOUT; | ||
301 | out: | ||
302 | /* clear fw mbx control register */ | ||
303 | QLCWRX(ahw, QLCNIC_FW_MBX_CTRL, QLCNIC_CLR_OWNER); | ||
304 | spin_unlock_irqrestore(&adapter->ahw->mbx_lock, flags); | ||
305 | return rsp; | ||
306 | } | ||
307 | |||
90 | static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter, | 308 | static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter, |
91 | int pci_using_dac) | 309 | int pci_using_dac) |
92 | { | 310 | { |
@@ -110,15 +328,29 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter, | |||
110 | if (err) | 328 | if (err) |
111 | goto err_out_disable_mbx_intr; | 329 | goto err_out_disable_mbx_intr; |
112 | 330 | ||
113 | err = qlcnic_setup_netdev(adapter, adapter->netdev, pci_using_dac); | 331 | err = qlcnic_sriov_cfg_bc_intr(adapter, 1); |
114 | if (err) | 332 | if (err) |
115 | goto err_out_cleanup_sriov; | 333 | goto err_out_cleanup_sriov; |
116 | 334 | ||
335 | err = qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_INIT); | ||
336 | if (err) | ||
337 | goto err_out_disable_bc_intr; | ||
338 | |||
339 | err = qlcnic_setup_netdev(adapter, adapter->netdev, pci_using_dac); | ||
340 | if (err) | ||
341 | goto err_out_send_channel_term; | ||
342 | |||
117 | pci_set_drvdata(adapter->pdev, adapter); | 343 | pci_set_drvdata(adapter->pdev, adapter); |
118 | dev_info(&adapter->pdev->dev, "%s: XGbE port initialized\n", | 344 | dev_info(&adapter->pdev->dev, "%s: XGbE port initialized\n", |
119 | adapter->netdev->name); | 345 | adapter->netdev->name); |
120 | return 0; | 346 | return 0; |
121 | 347 | ||
348 | err_out_send_channel_term: | ||
349 | qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_TERM); | ||
350 | |||
351 | err_out_disable_bc_intr: | ||
352 | qlcnic_sriov_cfg_bc_intr(adapter, 0); | ||
353 | |||
122 | err_out_cleanup_sriov: | 354 | err_out_cleanup_sriov: |
123 | __qlcnic_sriov_cleanup(adapter); | 355 | __qlcnic_sriov_cleanup(adapter); |
124 | 356 | ||
@@ -173,3 +405,720 @@ void qlcnic_sriov_vf_register_map(struct qlcnic_hardware_context *ahw) | |||
173 | ahw->reg_tbl = (u32 *)qlcnic_83xx_reg_tbl; | 405 | ahw->reg_tbl = (u32 *)qlcnic_83xx_reg_tbl; |
174 | ahw->ext_reg_tbl = (u32 *)qlcnic_83xx_ext_reg_tbl; | 406 | ahw->ext_reg_tbl = (u32 *)qlcnic_83xx_ext_reg_tbl; |
175 | } | 407 | } |
408 | |||
409 | static u32 qlcnic_sriov_get_bc_paysize(u32 real_pay_size, u8 curr_frag) | ||
410 | { | ||
411 | u32 pay_size; | ||
412 | |||
413 | pay_size = real_pay_size / ((curr_frag + 1) * QLC_BC_PAYLOAD_SZ); | ||
414 | |||
415 | if (pay_size) | ||
416 | pay_size = QLC_BC_PAYLOAD_SZ; | ||
417 | else | ||
418 | pay_size = real_pay_size % QLC_BC_PAYLOAD_SZ; | ||
419 | |||
420 | return pay_size; | ||
421 | } | ||
422 | |||
423 | int qlcnic_sriov_func_to_index(struct qlcnic_adapter *adapter, u8 pci_func) | ||
424 | { | ||
425 | struct qlcnic_vf_info *vf_info = adapter->ahw->sriov->vf_info; | ||
426 | u8 i; | ||
427 | |||
428 | if (qlcnic_sriov_vf_check(adapter)) | ||
429 | return 0; | ||
430 | |||
431 | for (i = 0; i < adapter->ahw->sriov->num_vfs; i++) { | ||
432 | if (vf_info[i].pci_func == pci_func) | ||
433 | return i; | ||
434 | } | ||
435 | |||
436 | return -EINVAL; | ||
437 | } | ||
438 | |||
439 | static inline int qlcnic_sriov_alloc_bc_trans(struct qlcnic_bc_trans **trans) | ||
440 | { | ||
441 | *trans = kzalloc(sizeof(struct qlcnic_bc_trans), GFP_ATOMIC); | ||
442 | if (!*trans) | ||
443 | return -ENOMEM; | ||
444 | |||
445 | init_completion(&(*trans)->resp_cmpl); | ||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | static inline int qlcnic_sriov_alloc_bc_msg(struct qlcnic_bc_hdr **hdr, | ||
450 | u32 size) | ||
451 | { | ||
452 | *hdr = kzalloc(sizeof(struct qlcnic_bc_hdr) * size, GFP_ATOMIC); | ||
453 | if (!*hdr) | ||
454 | return -ENOMEM; | ||
455 | |||
456 | return 0; | ||
457 | } | ||
458 | |||
459 | static int qlcnic_sriov_alloc_bc_mbx_args(struct qlcnic_cmd_args *mbx, u32 type) | ||
460 | { | ||
461 | const struct qlcnic_mailbox_metadata *mbx_tbl; | ||
462 | int i, size; | ||
463 | |||
464 | mbx_tbl = qlcnic_sriov_bc_mbx_tbl; | ||
465 | size = ARRAY_SIZE(qlcnic_sriov_bc_mbx_tbl); | ||
466 | |||
467 | for (i = 0; i < size; i++) { | ||
468 | if (type == mbx_tbl[i].cmd) { | ||
469 | mbx->op_type = QLC_BC_CMD; | ||
470 | mbx->req.num = mbx_tbl[i].in_args; | ||
471 | mbx->rsp.num = mbx_tbl[i].out_args; | ||
472 | mbx->req.arg = kcalloc(mbx->req.num, sizeof(u32), | ||
473 | GFP_ATOMIC); | ||
474 | if (!mbx->req.arg) | ||
475 | return -ENOMEM; | ||
476 | mbx->rsp.arg = kcalloc(mbx->rsp.num, sizeof(u32), | ||
477 | GFP_ATOMIC); | ||
478 | if (!mbx->rsp.arg) { | ||
479 | kfree(mbx->req.arg); | ||
480 | mbx->req.arg = NULL; | ||
481 | return -ENOMEM; | ||
482 | } | ||
483 | memset(mbx->req.arg, 0, sizeof(u32) * mbx->req.num); | ||
484 | memset(mbx->rsp.arg, 0, sizeof(u32) * mbx->rsp.num); | ||
485 | mbx->req.arg[0] = (type | (mbx->req.num << 16) | | ||
486 | (3 << 29)); | ||
487 | return 0; | ||
488 | } | ||
489 | } | ||
490 | return -EINVAL; | ||
491 | } | ||
492 | |||
493 | static int qlcnic_sriov_prepare_bc_hdr(struct qlcnic_bc_trans *trans, | ||
494 | struct qlcnic_cmd_args *cmd, | ||
495 | u16 seq, u8 msg_type) | ||
496 | { | ||
497 | struct qlcnic_bc_hdr *hdr; | ||
498 | int i; | ||
499 | u32 num_regs, bc_pay_sz; | ||
500 | u16 remainder; | ||
501 | u8 cmd_op, num_frags, t_num_frags; | ||
502 | |||
503 | bc_pay_sz = QLC_BC_PAYLOAD_SZ; | ||
504 | if (msg_type == QLC_BC_COMMAND) { | ||
505 | trans->req_pay = (struct qlcnic_bc_payload *)cmd->req.arg; | ||
506 | trans->rsp_pay = (struct qlcnic_bc_payload *)cmd->rsp.arg; | ||
507 | num_regs = cmd->req.num; | ||
508 | trans->req_pay_size = (num_regs * 4); | ||
509 | num_regs = cmd->rsp.num; | ||
510 | trans->rsp_pay_size = (num_regs * 4); | ||
511 | cmd_op = cmd->req.arg[0] & 0xff; | ||
512 | remainder = (trans->req_pay_size) % (bc_pay_sz); | ||
513 | num_frags = (trans->req_pay_size) / (bc_pay_sz); | ||
514 | if (remainder) | ||
515 | num_frags++; | ||
516 | t_num_frags = num_frags; | ||
517 | if (qlcnic_sriov_alloc_bc_msg(&trans->req_hdr, num_frags)) | ||
518 | return -ENOMEM; | ||
519 | remainder = (trans->rsp_pay_size) % (bc_pay_sz); | ||
520 | num_frags = (trans->rsp_pay_size) / (bc_pay_sz); | ||
521 | if (remainder) | ||
522 | num_frags++; | ||
523 | if (qlcnic_sriov_alloc_bc_msg(&trans->rsp_hdr, num_frags)) | ||
524 | return -ENOMEM; | ||
525 | num_frags = t_num_frags; | ||
526 | hdr = trans->req_hdr; | ||
527 | } else { | ||
528 | cmd->req.arg = (u32 *)trans->req_pay; | ||
529 | cmd->rsp.arg = (u32 *)trans->rsp_pay; | ||
530 | cmd_op = cmd->req.arg[0] & 0xff; | ||
531 | remainder = (trans->rsp_pay_size) % (bc_pay_sz); | ||
532 | num_frags = (trans->rsp_pay_size) / (bc_pay_sz); | ||
533 | if (remainder) | ||
534 | num_frags++; | ||
535 | cmd->req.num = trans->req_pay_size / 4; | ||
536 | cmd->rsp.num = trans->rsp_pay_size / 4; | ||
537 | hdr = trans->rsp_hdr; | ||
538 | } | ||
539 | |||
540 | trans->trans_id = seq; | ||
541 | trans->cmd_id = cmd_op; | ||
542 | for (i = 0; i < num_frags; i++) { | ||
543 | hdr[i].version = 2; | ||
544 | hdr[i].msg_type = msg_type; | ||
545 | hdr[i].op_type = cmd->op_type; | ||
546 | hdr[i].num_cmds = 1; | ||
547 | hdr[i].num_frags = num_frags; | ||
548 | hdr[i].frag_num = i + 1; | ||
549 | hdr[i].cmd_op = cmd_op; | ||
550 | hdr[i].seq_id = seq; | ||
551 | } | ||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | static void qlcnic_sriov_cleanup_transaction(struct qlcnic_bc_trans *trans) | ||
556 | { | ||
557 | if (!trans) | ||
558 | return; | ||
559 | kfree(trans->req_hdr); | ||
560 | kfree(trans->rsp_hdr); | ||
561 | kfree(trans); | ||
562 | } | ||
563 | |||
564 | static int qlcnic_sriov_clear_trans(struct qlcnic_vf_info *vf, | ||
565 | struct qlcnic_bc_trans *trans, u8 type) | ||
566 | { | ||
567 | struct qlcnic_trans_list *t_list; | ||
568 | unsigned long flags; | ||
569 | int ret = 0; | ||
570 | |||
571 | if (type == QLC_BC_RESPONSE) { | ||
572 | t_list = &vf->rcv_act; | ||
573 | spin_lock_irqsave(&t_list->lock, flags); | ||
574 | t_list->count--; | ||
575 | list_del(&trans->list); | ||
576 | if (t_list->count > 0) | ||
577 | ret = 1; | ||
578 | spin_unlock_irqrestore(&t_list->lock, flags); | ||
579 | } | ||
580 | if (type == QLC_BC_COMMAND) { | ||
581 | while (test_and_set_bit(QLC_BC_VF_SEND, &vf->state)) | ||
582 | msleep(100); | ||
583 | vf->send_cmd = NULL; | ||
584 | clear_bit(QLC_BC_VF_SEND, &vf->state); | ||
585 | } | ||
586 | return ret; | ||
587 | } | ||
588 | |||
589 | static void qlcnic_sriov_schedule_bc_cmd(struct qlcnic_sriov *sriov, | ||
590 | struct qlcnic_vf_info *vf, | ||
591 | work_func_t func) | ||
592 | { | ||
593 | INIT_WORK(&vf->trans_work, func); | ||
594 | queue_work(sriov->bc.bc_trans_wq, &vf->trans_work); | ||
595 | } | ||
596 | |||
597 | static inline void qlcnic_sriov_wait_for_resp(struct qlcnic_bc_trans *trans) | ||
598 | { | ||
599 | struct completion *cmpl = &trans->resp_cmpl; | ||
600 | |||
601 | if (wait_for_completion_timeout(cmpl, QLC_MBOX_RESP_TIMEOUT)) | ||
602 | trans->trans_state = QLC_END; | ||
603 | else | ||
604 | trans->trans_state = QLC_ABORT; | ||
605 | |||
606 | return; | ||
607 | } | ||
608 | |||
609 | static void qlcnic_sriov_handle_multi_frags(struct qlcnic_bc_trans *trans, | ||
610 | u8 type) | ||
611 | { | ||
612 | if (type == QLC_BC_RESPONSE) { | ||
613 | trans->curr_rsp_frag++; | ||
614 | if (trans->curr_rsp_frag < trans->rsp_hdr->num_frags) | ||
615 | trans->trans_state = QLC_INIT; | ||
616 | else | ||
617 | trans->trans_state = QLC_END; | ||
618 | } else { | ||
619 | trans->curr_req_frag++; | ||
620 | if (trans->curr_req_frag < trans->req_hdr->num_frags) | ||
621 | trans->trans_state = QLC_INIT; | ||
622 | else | ||
623 | trans->trans_state = QLC_WAIT_FOR_RESP; | ||
624 | } | ||
625 | } | ||
626 | |||
627 | static void qlcnic_sriov_wait_for_channel_free(struct qlcnic_bc_trans *trans, | ||
628 | u8 type) | ||
629 | { | ||
630 | struct qlcnic_vf_info *vf = trans->vf; | ||
631 | struct completion *cmpl = &vf->ch_free_cmpl; | ||
632 | |||
633 | if (!wait_for_completion_timeout(cmpl, QLC_MBOX_CH_FREE_TIMEOUT)) { | ||
634 | trans->trans_state = QLC_ABORT; | ||
635 | return; | ||
636 | } | ||
637 | |||
638 | clear_bit(QLC_BC_VF_CHANNEL, &vf->state); | ||
639 | qlcnic_sriov_handle_multi_frags(trans, type); | ||
640 | } | ||
641 | |||
642 | static void qlcnic_sriov_pull_bc_msg(struct qlcnic_adapter *adapter, | ||
643 | u32 *hdr, u32 *pay, u32 size) | ||
644 | { | ||
645 | struct qlcnic_hardware_context *ahw = adapter->ahw; | ||
646 | u32 fw_mbx; | ||
647 | u8 i, max = 2, hdr_size, j; | ||
648 | |||
649 | hdr_size = (sizeof(struct qlcnic_bc_hdr) / sizeof(u32)); | ||
650 | max = (size / sizeof(u32)) + hdr_size; | ||
651 | |||
652 | fw_mbx = readl(QLCNIC_MBX_FW(ahw, 0)); | ||
653 | for (i = 2, j = 0; j < hdr_size; i++, j++) | ||
654 | *(hdr++) = readl(QLCNIC_MBX_FW(ahw, i)); | ||
655 | for (; j < max; i++, j++) | ||
656 | *(pay++) = readl(QLCNIC_MBX_FW(ahw, i)); | ||
657 | } | ||
658 | |||
659 | static int __qlcnic_sriov_issue_bc_post(struct qlcnic_vf_info *vf) | ||
660 | { | ||
661 | int ret = -EBUSY; | ||
662 | u32 timeout = 10000; | ||
663 | |||
664 | do { | ||
665 | if (!test_and_set_bit(QLC_BC_VF_CHANNEL, &vf->state)) { | ||
666 | ret = 0; | ||
667 | break; | ||
668 | } | ||
669 | mdelay(1); | ||
670 | } while (--timeout); | ||
671 | |||
672 | return ret; | ||
673 | } | ||
674 | |||
675 | static int qlcnic_sriov_issue_bc_post(struct qlcnic_bc_trans *trans, u8 type) | ||
676 | { | ||
677 | struct qlcnic_vf_info *vf = trans->vf; | ||
678 | u32 pay_size, hdr_size; | ||
679 | u32 *hdr, *pay; | ||
680 | int ret; | ||
681 | u8 pci_func = trans->func_id; | ||
682 | |||
683 | if (__qlcnic_sriov_issue_bc_post(vf)) | ||
684 | return -EBUSY; | ||
685 | |||
686 | if (type == QLC_BC_COMMAND) { | ||
687 | hdr = (u32 *)(trans->req_hdr + trans->curr_req_frag); | ||
688 | pay = (u32 *)(trans->req_pay + trans->curr_req_frag); | ||
689 | hdr_size = (sizeof(struct qlcnic_bc_hdr) / sizeof(u32)); | ||
690 | pay_size = qlcnic_sriov_get_bc_paysize(trans->req_pay_size, | ||
691 | trans->curr_req_frag); | ||
692 | pay_size = (pay_size / sizeof(u32)); | ||
693 | } else { | ||
694 | hdr = (u32 *)(trans->rsp_hdr + trans->curr_rsp_frag); | ||
695 | pay = (u32 *)(trans->rsp_pay + trans->curr_rsp_frag); | ||
696 | hdr_size = (sizeof(struct qlcnic_bc_hdr) / sizeof(u32)); | ||
697 | pay_size = qlcnic_sriov_get_bc_paysize(trans->rsp_pay_size, | ||
698 | trans->curr_rsp_frag); | ||
699 | pay_size = (pay_size / sizeof(u32)); | ||
700 | } | ||
701 | |||
702 | ret = qlcnic_sriov_post_bc_msg(vf->adapter, hdr, pay, | ||
703 | pci_func, pay_size); | ||
704 | return ret; | ||
705 | } | ||
706 | |||
707 | static int __qlcnic_sriov_send_bc_msg(struct qlcnic_bc_trans *trans, | ||
708 | struct qlcnic_vf_info *vf, u8 type) | ||
709 | { | ||
710 | int err; | ||
711 | bool flag = true; | ||
712 | |||
713 | while (flag) { | ||
714 | switch (trans->trans_state) { | ||
715 | case QLC_INIT: | ||
716 | trans->trans_state = QLC_WAIT_FOR_CHANNEL_FREE; | ||
717 | if (qlcnic_sriov_issue_bc_post(trans, type)) | ||
718 | trans->trans_state = QLC_ABORT; | ||
719 | break; | ||
720 | case QLC_WAIT_FOR_CHANNEL_FREE: | ||
721 | qlcnic_sriov_wait_for_channel_free(trans, type); | ||
722 | break; | ||
723 | case QLC_WAIT_FOR_RESP: | ||
724 | qlcnic_sriov_wait_for_resp(trans); | ||
725 | break; | ||
726 | case QLC_END: | ||
727 | err = 0; | ||
728 | flag = false; | ||
729 | break; | ||
730 | case QLC_ABORT: | ||
731 | err = -EIO; | ||
732 | flag = false; | ||
733 | clear_bit(QLC_BC_VF_CHANNEL, &vf->state); | ||
734 | break; | ||
735 | default: | ||
736 | err = -EIO; | ||
737 | flag = false; | ||
738 | } | ||
739 | } | ||
740 | return err; | ||
741 | } | ||
742 | |||
743 | static int qlcnic_sriov_send_bc_cmd(struct qlcnic_adapter *adapter, | ||
744 | struct qlcnic_bc_trans *trans, int pci_func) | ||
745 | { | ||
746 | struct qlcnic_vf_info *vf; | ||
747 | int err, index = qlcnic_sriov_func_to_index(adapter, pci_func); | ||
748 | |||
749 | if (index < 0) | ||
750 | return -EIO; | ||
751 | |||
752 | vf = &adapter->ahw->sriov->vf_info[index]; | ||
753 | trans->vf = vf; | ||
754 | trans->func_id = pci_func; | ||
755 | |||
756 | if (!test_bit(QLC_BC_VF_STATE, &vf->state)) { | ||
757 | if (qlcnic_sriov_pf_check(adapter)) | ||
758 | return -EIO; | ||
759 | if (qlcnic_sriov_vf_check(adapter) && | ||
760 | trans->cmd_id != QLCNIC_BC_CMD_CHANNEL_INIT) | ||
761 | return -EIO; | ||
762 | } | ||
763 | |||
764 | mutex_lock(&vf->send_cmd_lock); | ||
765 | vf->send_cmd = trans; | ||
766 | err = __qlcnic_sriov_send_bc_msg(trans, vf, QLC_BC_COMMAND); | ||
767 | qlcnic_sriov_clear_trans(vf, trans, QLC_BC_COMMAND); | ||
768 | mutex_unlock(&vf->send_cmd_lock); | ||
769 | return err; | ||
770 | } | ||
771 | |||
772 | static void __qlcnic_sriov_process_bc_cmd(struct qlcnic_adapter *adapter, | ||
773 | struct qlcnic_bc_trans *trans, | ||
774 | struct qlcnic_cmd_args *cmd) | ||
775 | { | ||
776 | #ifdef CONFIG_QLCNIC_SRIOV | ||
777 | if (qlcnic_sriov_pf_check(adapter)) { | ||
778 | qlcnic_sriov_pf_process_bc_cmd(adapter, trans, cmd); | ||
779 | return; | ||
780 | } | ||
781 | #endif | ||
782 | cmd->rsp.arg[0] |= (0x9 << 25); | ||
783 | return; | ||
784 | } | ||
785 | |||
786 | static void qlcnic_sriov_process_bc_cmd(struct work_struct *work) | ||
787 | { | ||
788 | struct qlcnic_vf_info *vf = container_of(work, struct qlcnic_vf_info, | ||
789 | trans_work); | ||
790 | struct qlcnic_bc_trans *trans = NULL; | ||
791 | struct qlcnic_adapter *adapter = vf->adapter; | ||
792 | struct qlcnic_cmd_args cmd; | ||
793 | u8 req; | ||
794 | |||
795 | trans = list_first_entry(&vf->rcv_act.wait_list, | ||
796 | struct qlcnic_bc_trans, list); | ||
797 | adapter = vf->adapter; | ||
798 | |||
799 | if (qlcnic_sriov_prepare_bc_hdr(trans, &cmd, trans->req_hdr->seq_id, | ||
800 | QLC_BC_RESPONSE)) | ||
801 | goto cleanup_trans; | ||
802 | |||
803 | __qlcnic_sriov_process_bc_cmd(adapter, trans, &cmd); | ||
804 | trans->trans_state = QLC_INIT; | ||
805 | __qlcnic_sriov_send_bc_msg(trans, vf, QLC_BC_RESPONSE); | ||
806 | |||
807 | cleanup_trans: | ||
808 | qlcnic_free_mbx_args(&cmd); | ||
809 | req = qlcnic_sriov_clear_trans(vf, trans, QLC_BC_RESPONSE); | ||
810 | qlcnic_sriov_cleanup_transaction(trans); | ||
811 | if (req) | ||
812 | qlcnic_sriov_schedule_bc_cmd(adapter->ahw->sriov, vf, | ||
813 | qlcnic_sriov_process_bc_cmd); | ||
814 | } | ||
815 | |||
816 | static void qlcnic_sriov_handle_bc_resp(struct qlcnic_bc_hdr *hdr, | ||
817 | struct qlcnic_vf_info *vf) | ||
818 | { | ||
819 | struct qlcnic_bc_trans *trans; | ||
820 | u32 pay_size; | ||
821 | |||
822 | if (test_and_set_bit(QLC_BC_VF_SEND, &vf->state)) | ||
823 | return; | ||
824 | |||
825 | trans = vf->send_cmd; | ||
826 | |||
827 | if (trans == NULL) | ||
828 | goto clear_send; | ||
829 | |||
830 | if (trans->trans_id != hdr->seq_id) | ||
831 | goto clear_send; | ||
832 | |||
833 | pay_size = qlcnic_sriov_get_bc_paysize(trans->rsp_pay_size, | ||
834 | trans->curr_rsp_frag); | ||
835 | qlcnic_sriov_pull_bc_msg(vf->adapter, | ||
836 | (u32 *)(trans->rsp_hdr + trans->curr_rsp_frag), | ||
837 | (u32 *)(trans->rsp_pay + trans->curr_rsp_frag), | ||
838 | pay_size); | ||
839 | if (++trans->curr_rsp_frag < trans->rsp_hdr->num_frags) | ||
840 | goto clear_send; | ||
841 | |||
842 | complete(&trans->resp_cmpl); | ||
843 | |||
844 | clear_send: | ||
845 | clear_bit(QLC_BC_VF_SEND, &vf->state); | ||
846 | } | ||
847 | |||
848 | static int qlcnic_sriov_add_act_list(struct qlcnic_sriov *sriov, | ||
849 | struct qlcnic_vf_info *vf, | ||
850 | struct qlcnic_bc_trans *trans) | ||
851 | { | ||
852 | struct qlcnic_trans_list *t_list = &vf->rcv_act; | ||
853 | |||
854 | spin_lock(&t_list->lock); | ||
855 | t_list->count++; | ||
856 | list_add_tail(&trans->list, &t_list->wait_list); | ||
857 | if (t_list->count == 1) | ||
858 | qlcnic_sriov_schedule_bc_cmd(sriov, vf, | ||
859 | qlcnic_sriov_process_bc_cmd); | ||
860 | spin_unlock(&t_list->lock); | ||
861 | return 0; | ||
862 | } | ||
863 | |||
864 | static void qlcnic_sriov_handle_pending_trans(struct qlcnic_sriov *sriov, | ||
865 | struct qlcnic_vf_info *vf, | ||
866 | struct qlcnic_bc_hdr *hdr) | ||
867 | { | ||
868 | struct qlcnic_bc_trans *trans = NULL; | ||
869 | struct list_head *node; | ||
870 | u32 pay_size, curr_frag; | ||
871 | u8 found = 0, active = 0; | ||
872 | |||
873 | spin_lock(&vf->rcv_pend.lock); | ||
874 | if (vf->rcv_pend.count > 0) { | ||
875 | list_for_each(node, &vf->rcv_pend.wait_list) { | ||
876 | trans = list_entry(node, struct qlcnic_bc_trans, list); | ||
877 | if (trans->trans_id == hdr->seq_id) { | ||
878 | found = 1; | ||
879 | break; | ||
880 | } | ||
881 | } | ||
882 | } | ||
883 | |||
884 | if (found) { | ||
885 | curr_frag = trans->curr_req_frag; | ||
886 | pay_size = qlcnic_sriov_get_bc_paysize(trans->req_pay_size, | ||
887 | curr_frag); | ||
888 | qlcnic_sriov_pull_bc_msg(vf->adapter, | ||
889 | (u32 *)(trans->req_hdr + curr_frag), | ||
890 | (u32 *)(trans->req_pay + curr_frag), | ||
891 | pay_size); | ||
892 | trans->curr_req_frag++; | ||
893 | if (trans->curr_req_frag >= hdr->num_frags) { | ||
894 | vf->rcv_pend.count--; | ||
895 | list_del(&trans->list); | ||
896 | active = 1; | ||
897 | } | ||
898 | } | ||
899 | spin_unlock(&vf->rcv_pend.lock); | ||
900 | |||
901 | if (active) | ||
902 | if (qlcnic_sriov_add_act_list(sriov, vf, trans)) | ||
903 | qlcnic_sriov_cleanup_transaction(trans); | ||
904 | |||
905 | return; | ||
906 | } | ||
907 | |||
908 | static void qlcnic_sriov_handle_bc_cmd(struct qlcnic_sriov *sriov, | ||
909 | struct qlcnic_bc_hdr *hdr, | ||
910 | struct qlcnic_vf_info *vf) | ||
911 | { | ||
912 | struct qlcnic_bc_trans *trans; | ||
913 | struct qlcnic_adapter *adapter = vf->adapter; | ||
914 | struct qlcnic_cmd_args cmd; | ||
915 | u32 pay_size; | ||
916 | int err; | ||
917 | u8 cmd_op; | ||
918 | |||
919 | if (!test_bit(QLC_BC_VF_STATE, &vf->state) && | ||
920 | hdr->op_type != QLC_BC_CMD && | ||
921 | hdr->cmd_op != QLCNIC_BC_CMD_CHANNEL_INIT) | ||
922 | return; | ||
923 | |||
924 | if (hdr->frag_num > 1) { | ||
925 | qlcnic_sriov_handle_pending_trans(sriov, vf, hdr); | ||
926 | return; | ||
927 | } | ||
928 | |||
929 | cmd_op = hdr->cmd_op; | ||
930 | if (qlcnic_sriov_alloc_bc_trans(&trans)) | ||
931 | return; | ||
932 | |||
933 | if (hdr->op_type == QLC_BC_CMD) | ||
934 | err = qlcnic_sriov_alloc_bc_mbx_args(&cmd, cmd_op); | ||
935 | else | ||
936 | err = qlcnic_alloc_mbx_args(&cmd, adapter, cmd_op); | ||
937 | |||
938 | if (err) { | ||
939 | qlcnic_sriov_cleanup_transaction(trans); | ||
940 | return; | ||
941 | } | ||
942 | |||
943 | cmd.op_type = hdr->op_type; | ||
944 | if (qlcnic_sriov_prepare_bc_hdr(trans, &cmd, hdr->seq_id, | ||
945 | QLC_BC_COMMAND)) { | ||
946 | qlcnic_free_mbx_args(&cmd); | ||
947 | qlcnic_sriov_cleanup_transaction(trans); | ||
948 | return; | ||
949 | } | ||
950 | |||
951 | pay_size = qlcnic_sriov_get_bc_paysize(trans->req_pay_size, | ||
952 | trans->curr_req_frag); | ||
953 | qlcnic_sriov_pull_bc_msg(vf->adapter, | ||
954 | (u32 *)(trans->req_hdr + trans->curr_req_frag), | ||
955 | (u32 *)(trans->req_pay + trans->curr_req_frag), | ||
956 | pay_size); | ||
957 | trans->func_id = vf->pci_func; | ||
958 | trans->vf = vf; | ||
959 | trans->trans_id = hdr->seq_id; | ||
960 | trans->curr_req_frag++; | ||
961 | if (trans->curr_req_frag == trans->req_hdr->num_frags) { | ||
962 | if (qlcnic_sriov_add_act_list(sriov, vf, trans)) { | ||
963 | qlcnic_free_mbx_args(&cmd); | ||
964 | qlcnic_sriov_cleanup_transaction(trans); | ||
965 | } | ||
966 | } else { | ||
967 | spin_lock(&vf->rcv_pend.lock); | ||
968 | list_add_tail(&trans->list, &vf->rcv_pend.wait_list); | ||
969 | vf->rcv_pend.count++; | ||
970 | spin_unlock(&vf->rcv_pend.lock); | ||
971 | } | ||
972 | } | ||
973 | |||
974 | static void qlcnic_sriov_handle_msg_event(struct qlcnic_sriov *sriov, | ||
975 | struct qlcnic_vf_info *vf) | ||
976 | { | ||
977 | struct qlcnic_bc_hdr hdr; | ||
978 | u32 *ptr = (u32 *)&hdr; | ||
979 | u8 msg_type, i; | ||
980 | |||
981 | for (i = 2; i < 6; i++) | ||
982 | ptr[i - 2] = readl(QLCNIC_MBX_FW(vf->adapter->ahw, i)); | ||
983 | msg_type = hdr.msg_type; | ||
984 | |||
985 | switch (msg_type) { | ||
986 | case QLC_BC_COMMAND: | ||
987 | qlcnic_sriov_handle_bc_cmd(sriov, &hdr, vf); | ||
988 | break; | ||
989 | case QLC_BC_RESPONSE: | ||
990 | qlcnic_sriov_handle_bc_resp(&hdr, vf); | ||
991 | break; | ||
992 | } | ||
993 | } | ||
994 | |||
995 | void qlcnic_sriov_handle_bc_event(struct qlcnic_adapter *adapter, u32 event) | ||
996 | { | ||
997 | struct qlcnic_vf_info *vf; | ||
998 | struct qlcnic_sriov *sriov; | ||
999 | int index; | ||
1000 | u8 pci_func; | ||
1001 | |||
1002 | sriov = adapter->ahw->sriov; | ||
1003 | pci_func = qlcnic_sriov_target_func_id(event); | ||
1004 | index = qlcnic_sriov_func_to_index(adapter, pci_func); | ||
1005 | |||
1006 | if (index < 0) | ||
1007 | return; | ||
1008 | |||
1009 | vf = &sriov->vf_info[index]; | ||
1010 | vf->pci_func = pci_func; | ||
1011 | |||
1012 | if (qlcnic_sriov_channel_free_check(event)) | ||
1013 | complete(&vf->ch_free_cmpl); | ||
1014 | |||
1015 | if (qlcnic_sriov_bc_msg_check(event)) | ||
1016 | qlcnic_sriov_handle_msg_event(sriov, vf); | ||
1017 | } | ||
1018 | |||
1019 | int qlcnic_sriov_cfg_bc_intr(struct qlcnic_adapter *adapter, u8 enable) | ||
1020 | { | ||
1021 | struct qlcnic_cmd_args cmd; | ||
1022 | int err; | ||
1023 | |||
1024 | if (!test_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state)) | ||
1025 | return 0; | ||
1026 | |||
1027 | if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_BC_EVENT_SETUP)) | ||
1028 | return -ENOMEM; | ||
1029 | |||
1030 | if (enable) | ||
1031 | cmd.req.arg[1] = (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7); | ||
1032 | |||
1033 | err = qlcnic_83xx_mbx_op(adapter, &cmd); | ||
1034 | |||
1035 | if (err != QLCNIC_RCODE_SUCCESS) { | ||
1036 | dev_err(&adapter->pdev->dev, | ||
1037 | "Failed to %s bc events, err=%d\n", | ||
1038 | (enable ? "enable" : "disable"), err); | ||
1039 | } | ||
1040 | |||
1041 | qlcnic_free_mbx_args(&cmd); | ||
1042 | return err; | ||
1043 | } | ||
1044 | |||
1045 | static int qlcnic_sriov_vf_mbx_op(struct qlcnic_adapter *adapter, | ||
1046 | struct qlcnic_cmd_args *cmd) | ||
1047 | { | ||
1048 | struct qlcnic_bc_trans *trans; | ||
1049 | int err; | ||
1050 | u32 rsp_data, opcode, mbx_err_code, rsp; | ||
1051 | u16 seq = ++adapter->ahw->sriov->bc.trans_counter; | ||
1052 | |||
1053 | if (qlcnic_sriov_alloc_bc_trans(&trans)) | ||
1054 | return -ENOMEM; | ||
1055 | |||
1056 | if (qlcnic_sriov_prepare_bc_hdr(trans, cmd, seq, QLC_BC_COMMAND)) | ||
1057 | return -ENOMEM; | ||
1058 | |||
1059 | if (!test_bit(QLC_83XX_MBX_READY, &adapter->ahw->idc.status)) { | ||
1060 | rsp = -EIO; | ||
1061 | QLCDB(adapter, DRV, "MBX not Ready!(cmd 0x%x) for VF 0x%x\n", | ||
1062 | QLCNIC_MBX_RSP(cmd->req.arg[0]), adapter->ahw->pci_func); | ||
1063 | goto err_out; | ||
1064 | } | ||
1065 | |||
1066 | err = qlcnic_sriov_send_bc_cmd(adapter, trans, adapter->ahw->pci_func); | ||
1067 | if (err) { | ||
1068 | dev_err(&adapter->pdev->dev, | ||
1069 | "MBX command 0x%x timed out for VF %d\n", | ||
1070 | (cmd->req.arg[0] & 0xffff), adapter->ahw->pci_func); | ||
1071 | rsp = QLCNIC_RCODE_TIMEOUT; | ||
1072 | goto err_out; | ||
1073 | } | ||
1074 | |||
1075 | rsp_data = cmd->rsp.arg[0]; | ||
1076 | mbx_err_code = QLCNIC_MBX_STATUS(rsp_data); | ||
1077 | opcode = QLCNIC_MBX_RSP(cmd->req.arg[0]); | ||
1078 | |||
1079 | if ((mbx_err_code == QLCNIC_MBX_RSP_OK) || | ||
1080 | (mbx_err_code == QLCNIC_MBX_PORT_RSP_OK)) { | ||
1081 | rsp = QLCNIC_RCODE_SUCCESS; | ||
1082 | } else { | ||
1083 | rsp = mbx_err_code; | ||
1084 | if (!rsp) | ||
1085 | rsp = 1; | ||
1086 | dev_err(&adapter->pdev->dev, | ||
1087 | "MBX command 0x%x failed with err:0x%x for VF %d\n", | ||
1088 | opcode, mbx_err_code, adapter->ahw->pci_func); | ||
1089 | } | ||
1090 | |||
1091 | err_out: | ||
1092 | qlcnic_sriov_cleanup_transaction(trans); | ||
1093 | return rsp; | ||
1094 | } | ||
1095 | |||
1096 | int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *adapter, u8 cmd_op) | ||
1097 | { | ||
1098 | struct qlcnic_cmd_args cmd; | ||
1099 | struct qlcnic_vf_info *vf = &adapter->ahw->sriov->vf_info[0]; | ||
1100 | int ret; | ||
1101 | |||
1102 | if (qlcnic_sriov_alloc_bc_mbx_args(&cmd, cmd_op)) | ||
1103 | return -ENOMEM; | ||
1104 | |||
1105 | ret = qlcnic_issue_cmd(adapter, &cmd); | ||
1106 | if (ret) { | ||
1107 | dev_err(&adapter->pdev->dev, | ||
1108 | "Failed bc channel %s %d\n", cmd_op ? "term" : "init", | ||
1109 | ret); | ||
1110 | goto out; | ||
1111 | } | ||
1112 | |||
1113 | cmd_op = (cmd.rsp.arg[0] & 0xff); | ||
1114 | if (cmd.rsp.arg[0] >> 25 == 2) | ||
1115 | return 2; | ||
1116 | if (cmd_op == QLCNIC_BC_CMD_CHANNEL_INIT) | ||
1117 | set_bit(QLC_BC_VF_STATE, &vf->state); | ||
1118 | else | ||
1119 | clear_bit(QLC_BC_VF_STATE, &vf->state); | ||
1120 | |||
1121 | out: | ||
1122 | qlcnic_free_mbx_args(&cmd); | ||
1123 | return ret; | ||
1124 | } | ||
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c index aa5ba6ec4d87..87ff58d29155 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c | |||
@@ -13,6 +13,10 @@ | |||
13 | 13 | ||
14 | static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *, u8); | 14 | static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *, u8); |
15 | 15 | ||
16 | struct qlcnic_sriov_cmd_handler { | ||
17 | int (*fn) (struct qlcnic_bc_trans *, struct qlcnic_cmd_args *); | ||
18 | }; | ||
19 | |||
16 | static int qlcnic_sriov_pf_set_vport_info(struct qlcnic_adapter *adapter, | 20 | static int qlcnic_sriov_pf_set_vport_info(struct qlcnic_adapter *adapter, |
17 | struct qlcnic_info *npar_info, | 21 | struct qlcnic_info *npar_info, |
18 | u16 vport_id) | 22 | u16 vport_id) |
@@ -174,27 +178,54 @@ static void qlcnic_sriov_pf_reset_vport_handle(struct qlcnic_adapter *adapter, | |||
174 | u8 func) | 178 | u8 func) |
175 | { | 179 | { |
176 | struct qlcnic_sriov *sriov = adapter->ahw->sriov; | 180 | struct qlcnic_sriov *sriov = adapter->ahw->sriov; |
181 | struct qlcnic_vport *vp; | ||
182 | int index; | ||
177 | 183 | ||
178 | if (adapter->ahw->pci_func == func) | 184 | if (adapter->ahw->pci_func == func) { |
179 | sriov->vp_handle = 0; | 185 | sriov->vp_handle = 0; |
186 | } else { | ||
187 | index = qlcnic_sriov_func_to_index(adapter, func); | ||
188 | if (index < 0) | ||
189 | return; | ||
190 | vp = sriov->vf_info[index].vp; | ||
191 | vp->handle = 0; | ||
192 | } | ||
180 | } | 193 | } |
181 | 194 | ||
182 | static void qlcnic_sriov_pf_set_vport_handle(struct qlcnic_adapter *adapter, | 195 | static void qlcnic_sriov_pf_set_vport_handle(struct qlcnic_adapter *adapter, |
183 | u16 vport_handle, u8 func) | 196 | u16 vport_handle, u8 func) |
184 | { | 197 | { |
185 | struct qlcnic_sriov *sriov = adapter->ahw->sriov; | 198 | struct qlcnic_sriov *sriov = adapter->ahw->sriov; |
199 | struct qlcnic_vport *vp; | ||
200 | int index; | ||
186 | 201 | ||
187 | if (adapter->ahw->pci_func == func) | 202 | if (adapter->ahw->pci_func == func) { |
188 | sriov->vp_handle = vport_handle; | 203 | sriov->vp_handle = vport_handle; |
204 | } else { | ||
205 | index = qlcnic_sriov_func_to_index(adapter, func); | ||
206 | if (index < 0) | ||
207 | return; | ||
208 | vp = sriov->vf_info[index].vp; | ||
209 | vp->handle = vport_handle; | ||
210 | } | ||
189 | } | 211 | } |
190 | 212 | ||
191 | static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *adapter, | 213 | static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *adapter, |
192 | u8 func) | 214 | u8 func) |
193 | { | 215 | { |
194 | struct qlcnic_sriov *sriov = adapter->ahw->sriov; | 216 | struct qlcnic_sriov *sriov = adapter->ahw->sriov; |
217 | struct qlcnic_vf_info *vf_info; | ||
218 | int index; | ||
195 | 219 | ||
196 | if (adapter->ahw->pci_func == func) | 220 | if (adapter->ahw->pci_func == func) { |
197 | return sriov->vp_handle; | 221 | return sriov->vp_handle; |
222 | } else { | ||
223 | index = qlcnic_sriov_func_to_index(adapter, func); | ||
224 | if (index >= 0) { | ||
225 | vf_info = &sriov->vf_info[index]; | ||
226 | return vf_info->vp->handle; | ||
227 | } | ||
228 | } | ||
198 | 229 | ||
199 | return -EINVAL; | 230 | return -EINVAL; |
200 | } | 231 | } |
@@ -273,6 +304,7 @@ void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) | |||
273 | if (!qlcnic_sriov_enable_check(adapter)) | 304 | if (!qlcnic_sriov_enable_check(adapter)) |
274 | return; | 305 | return; |
275 | 306 | ||
307 | qlcnic_sriov_cfg_bc_intr(adapter, 0); | ||
276 | qlcnic_sriov_pf_config_vport(adapter, 0, func); | 308 | qlcnic_sriov_pf_config_vport(adapter, 0, func); |
277 | qlcnic_sriov_pf_cfg_eswitch(adapter, func, 0); | 309 | qlcnic_sriov_pf_cfg_eswitch(adapter, func, 0); |
278 | __qlcnic_sriov_cleanup(adapter); | 310 | __qlcnic_sriov_cleanup(adapter); |
@@ -349,6 +381,10 @@ static int qlcnic_sriov_pf_init(struct qlcnic_adapter *adapter) | |||
349 | if (err) | 381 | if (err) |
350 | goto delete_vport; | 382 | goto delete_vport; |
351 | 383 | ||
384 | err = qlcnic_sriov_cfg_bc_intr(adapter, 1); | ||
385 | if (err) | ||
386 | goto delete_vport; | ||
387 | |||
352 | ahw->physical_port = (u8) nic_info.phys_port; | 388 | ahw->physical_port = (u8) nic_info.phys_port; |
353 | ahw->switch_mode = nic_info.switch_mode; | 389 | ahw->switch_mode = nic_info.switch_mode; |
354 | ahw->max_mtu = nic_info.max_mtu; | 390 | ahw->max_mtu = nic_info.max_mtu; |
@@ -453,3 +489,79 @@ int qlcnic_pci_sriov_configure(struct pci_dev *dev, int num_vfs) | |||
453 | clear_bit(__QLCNIC_RESETTING, &adapter->state); | 489 | clear_bit(__QLCNIC_RESETTING, &adapter->state); |
454 | return err; | 490 | return err; |
455 | } | 491 | } |
492 | |||
493 | static int qlcnic_sriov_set_vf_vport_info(struct qlcnic_adapter *adapter, | ||
494 | u16 func) | ||
495 | { | ||
496 | struct qlcnic_info defvp_info; | ||
497 | int err; | ||
498 | |||
499 | err = qlcnic_sriov_pf_cal_res_limit(adapter, &defvp_info, func); | ||
500 | if (err) | ||
501 | return -EIO; | ||
502 | |||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | static int qlcnic_sriov_pf_channel_cfg_cmd(struct qlcnic_bc_trans *trans, | ||
507 | struct qlcnic_cmd_args *cmd) | ||
508 | { | ||
509 | struct qlcnic_vf_info *vf = trans->vf; | ||
510 | struct qlcnic_adapter *adapter = vf->adapter; | ||
511 | int err; | ||
512 | u16 func = vf->pci_func; | ||
513 | |||
514 | cmd->rsp.arg[0] = trans->req_hdr->cmd_op; | ||
515 | cmd->rsp.arg[0] |= (1 << 16); | ||
516 | |||
517 | if (trans->req_hdr->cmd_op == QLCNIC_BC_CMD_CHANNEL_INIT) { | ||
518 | err = qlcnic_sriov_pf_config_vport(adapter, 1, func); | ||
519 | if (!err) { | ||
520 | err = qlcnic_sriov_set_vf_vport_info(adapter, func); | ||
521 | if (err) | ||
522 | qlcnic_sriov_pf_config_vport(adapter, 0, func); | ||
523 | } | ||
524 | } else { | ||
525 | err = qlcnic_sriov_pf_config_vport(adapter, 0, func); | ||
526 | } | ||
527 | |||
528 | if (err) | ||
529 | goto err_out; | ||
530 | |||
531 | cmd->rsp.arg[0] |= (1 << 25); | ||
532 | |||
533 | if (trans->req_hdr->cmd_op == QLCNIC_BC_CMD_CHANNEL_INIT) | ||
534 | set_bit(QLC_BC_VF_STATE, &vf->state); | ||
535 | else | ||
536 | clear_bit(QLC_BC_VF_STATE, &vf->state); | ||
537 | |||
538 | return err; | ||
539 | |||
540 | err_out: | ||
541 | cmd->rsp.arg[0] |= (2 << 25); | ||
542 | return err; | ||
543 | } | ||
544 | |||
545 | static const struct qlcnic_sriov_cmd_handler qlcnic_pf_bc_cmd_hdlr[] = { | ||
546 | [QLCNIC_BC_CMD_CHANNEL_INIT] = {&qlcnic_sriov_pf_channel_cfg_cmd}, | ||
547 | [QLCNIC_BC_CMD_CHANNEL_TERM] = {&qlcnic_sriov_pf_channel_cfg_cmd}, | ||
548 | }; | ||
549 | |||
550 | void qlcnic_sriov_pf_process_bc_cmd(struct qlcnic_adapter *adapter, | ||
551 | struct qlcnic_bc_trans *trans, | ||
552 | struct qlcnic_cmd_args *cmd) | ||
553 | { | ||
554 | u8 size, cmd_op; | ||
555 | |||
556 | cmd_op = trans->req_hdr->cmd_op; | ||
557 | |||
558 | if (trans->req_hdr->op_type == QLC_BC_CMD) { | ||
559 | size = ARRAY_SIZE(qlcnic_pf_bc_cmd_hdlr); | ||
560 | if (cmd_op < size) { | ||
561 | qlcnic_pf_bc_cmd_hdlr[cmd_op].fn(trans, cmd); | ||
562 | return; | ||
563 | } | ||
564 | } | ||
565 | |||
566 | cmd->rsp.arg[0] |= (0x9 << 25); | ||
567 | } | ||