Commit | Line | Data |
---|---|---|
7f966452 SC |
1 | #include "qlcnic.h" |
2 | #include <linux/if_vlan.h> | |
3 | #include <linux/ipv6.h> | |
4 | #include <linux/ethtool.h> | |
5 | #include <linux/interrupt.h> | |
6 | ||
7 | #define QLCNIC_MAX_TX_QUEUES 1 | |
8 | ||
9 | #define QLCNIC_MBX_RSP(reg) LSW(reg) | |
10 | #define QLCNIC_MBX_NUM_REGS(reg) (MSW(reg) & 0x1FF) | |
11 | #define QLCNIC_MBX_STATUS(reg) (((reg) >> 25) & 0x7F) | |
12 | #define QLCNIC_MBX_HOST(ahw, i) ((ahw)->pci_base0 + ((i) * 4)) | |
13 | #define QLCNIC_MBX_FW(ahw, i) ((ahw)->pci_base0 + 0x800 + ((i) * 4)) | |
14 | ||
15 | #define RSS_HASHTYPE_IP_TCP 0x3 | |
16 | ||
17 | /* status descriptor mailbox data | |
18 | * @phy_addr: physical address of buffer | |
19 | * @sds_ring_size: buffer size | |
20 | * @intrpt_id: interrupt id | |
21 | * @intrpt_val: source of interrupt | |
22 | */ | |
23 | struct qlcnic_sds_mbx { | |
24 | u64 phy_addr; | |
25 | u8 rsvd1[16]; | |
26 | u16 sds_ring_size; | |
27 | u16 rsvd2[3]; | |
28 | u16 intrpt_id; | |
29 | u8 intrpt_val; | |
30 | u8 rsvd3[5]; | |
31 | } __packed; | |
32 | ||
33 | /* receive descriptor buffer data | |
34 | * phy_addr_reg: physical address of regular buffer | |
35 | * phy_addr_jmb: physical address of jumbo buffer | |
36 | * reg_ring_sz: size of regular buffer | |
37 | * reg_ring_len: no. of entries in regular buffer | |
38 | * jmb_ring_len: no. of entries in jumbo buffer | |
39 | * jmb_ring_sz: size of jumbo buffer | |
40 | */ | |
41 | struct qlcnic_rds_mbx { | |
42 | u64 phy_addr_reg; | |
43 | u64 phy_addr_jmb; | |
44 | u16 reg_ring_sz; | |
45 | u16 reg_ring_len; | |
46 | u16 jmb_ring_sz; | |
47 | u16 jmb_ring_len; | |
48 | } __packed; | |
49 | ||
50 | /* host producers for regular and jumbo rings */ | |
51 | struct __host_producer_mbx { | |
52 | u32 reg_buf; | |
53 | u32 jmb_buf; | |
54 | } __packed; | |
55 | ||
56 | /* Receive context mailbox data outbox registers | |
57 | * @state: state of the context | |
58 | * @vport_id: virtual port id | |
59 | * @context_id: receive context id | |
60 | * @num_pci_func: number of pci functions of the port | |
61 | * @phy_port: physical port id | |
62 | */ | |
63 | struct qlcnic_rcv_mbx_out { | |
64 | u8 rcv_num; | |
65 | u8 sts_num; | |
66 | u16 ctx_id; | |
67 | u8 state; | |
68 | u8 num_pci_func; | |
69 | u8 phy_port; | |
70 | u8 vport_id; | |
71 | u32 host_csmr[QLCNIC_MAX_RING_SETS]; | |
72 | struct __host_producer_mbx host_prod[QLCNIC_MAX_RING_SETS]; | |
73 | } __packed; | |
74 | ||
75 | struct qlcnic_add_rings_mbx_out { | |
76 | u8 rcv_num; | |
77 | u8 sts_num; | |
78 | u16 ctx_id; | |
79 | u32 host_csmr[QLCNIC_MAX_RING_SETS]; | |
80 | struct __host_producer_mbx host_prod[QLCNIC_MAX_RING_SETS]; | |
81 | } __packed; | |
82 | ||
83 | /* Transmit context mailbox inbox registers | |
84 | * @phys_addr: DMA address of the transmit buffer | |
85 | * @cnsmr_index: host consumer index | |
86 | * @size: legth of transmit buffer ring | |
87 | * @intr_id: interrput id | |
88 | * @src: src of interrupt | |
89 | */ | |
90 | struct qlcnic_tx_mbx { | |
91 | u64 phys_addr; | |
92 | u64 cnsmr_index; | |
93 | u16 size; | |
94 | u16 intr_id; | |
95 | u8 src; | |
96 | u8 rsvd[3]; | |
97 | } __packed; | |
98 | ||
99 | /* Transmit context mailbox outbox registers | |
100 | * @host_prod: host producer index | |
101 | * @ctx_id: transmit context id | |
102 | * @state: state of the transmit context | |
103 | */ | |
104 | struct qlcnic_tx_mbx_out { | |
105 | u32 host_prod; | |
106 | u16 ctx_id; | |
107 | u8 state; | |
108 | u8 rsvd; | |
109 | } __packed; | |
110 | ||
111 | static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = { | |
112 | {QLCNIC_CMD_CONFIGURE_IP_ADDR, 6, 1}, | |
113 | {QLCNIC_CMD_CONFIG_INTRPT, 18, 34}, | |
114 | {QLCNIC_CMD_CREATE_RX_CTX, 136, 27}, | |
115 | {QLCNIC_CMD_DESTROY_RX_CTX, 2, 1}, | |
116 | {QLCNIC_CMD_CREATE_TX_CTX, 54, 18}, | |
117 | {QLCNIC_CMD_DESTROY_TX_CTX, 2, 1}, | |
118 | {QLCNIC_CMD_CONFIGURE_MAC_LEARNING, 2, 1}, | |
119 | {QLCNIC_CMD_INTRPT_TEST, 22, 12}, | |
120 | {QLCNIC_CMD_SET_MTU, 3, 1}, | |
121 | {QLCNIC_CMD_READ_PHY, 4, 2}, | |
122 | {QLCNIC_CMD_WRITE_PHY, 5, 1}, | |
123 | {QLCNIC_CMD_READ_HW_REG, 4, 1}, | |
124 | {QLCNIC_CMD_GET_FLOW_CTL, 4, 2}, | |
125 | {QLCNIC_CMD_SET_FLOW_CTL, 4, 1}, | |
126 | {QLCNIC_CMD_READ_MAX_MTU, 4, 2}, | |
127 | {QLCNIC_CMD_READ_MAX_LRO, 4, 2}, | |
128 | {QLCNIC_CMD_MAC_ADDRESS, 4, 3}, | |
129 | {QLCNIC_CMD_GET_PCI_INFO, 1, 66}, | |
130 | {QLCNIC_CMD_GET_NIC_INFO, 2, 19}, | |
131 | {QLCNIC_CMD_SET_NIC_INFO, 32, 1}, | |
132 | {QLCNIC_CMD_GET_ESWITCH_CAPABILITY, 4, 3}, | |
133 | {QLCNIC_CMD_TOGGLE_ESWITCH, 4, 1}, | |
134 | {QLCNIC_CMD_GET_ESWITCH_STATUS, 4, 3}, | |
135 | {QLCNIC_CMD_SET_PORTMIRRORING, 4, 1}, | |
136 | {QLCNIC_CMD_CONFIGURE_ESWITCH, 4, 1}, | |
137 | {QLCNIC_CMD_GET_ESWITCH_PORT_CONFIG, 4, 3}, | |
138 | {QLCNIC_CMD_GET_ESWITCH_STATS, 5, 1}, | |
139 | {QLCNIC_CMD_CONFIG_PORT, 4, 1}, | |
140 | {QLCNIC_CMD_TEMP_SIZE, 1, 4}, | |
141 | {QLCNIC_CMD_GET_TEMP_HDR, 5, 5}, | |
142 | {QLCNIC_CMD_GET_LINK_EVENT, 2, 1}, | |
143 | {QLCNIC_CMD_CONFIG_MAC_VLAN, 4, 3}, | |
144 | {QLCNIC_CMD_CONFIG_INTR_COAL, 6, 1}, | |
145 | {QLCNIC_CMD_CONFIGURE_RSS, 14, 1}, | |
146 | {QLCNIC_CMD_CONFIGURE_LED, 2, 1}, | |
147 | {QLCNIC_CMD_CONFIGURE_MAC_RX_MODE, 2, 1}, | |
148 | {QLCNIC_CMD_CONFIGURE_HW_LRO, 2, 1}, | |
149 | {QLCNIC_CMD_GET_STATISTICS, 2, 80}, | |
150 | {QLCNIC_CMD_SET_PORT_CONFIG, 2, 1}, | |
151 | {QLCNIC_CMD_GET_PORT_CONFIG, 2, 2}, | |
152 | {QLCNIC_CMD_GET_LINK_STATUS, 2, 4}, | |
153 | {QLCNIC_CMD_IDC_ACK, 5, 1}, | |
154 | {QLCNIC_CMD_INIT_NIC_FUNC, 2, 1}, | |
155 | {QLCNIC_CMD_STOP_NIC_FUNC, 2, 1}, | |
156 | {QLCNIC_CMD_SET_LED_CONFIG, 5, 1}, | |
157 | {QLCNIC_CMD_GET_LED_CONFIG, 1, 5}, | |
158 | {QLCNIC_CMD_ADD_RCV_RINGS, 130, 26}, | |
159 | }; | |
160 | ||
161 | static const u32 qlcnic_83xx_ext_reg_tbl[] = { | |
162 | 0x38CC, /* Global Reset */ | |
163 | 0x38F0, /* Wildcard */ | |
164 | 0x38FC, /* Informant */ | |
165 | 0x3038, /* Host MBX ctrl */ | |
166 | 0x303C, /* FW MBX ctrl */ | |
167 | 0x355C, /* BOOT LOADER ADDRESS REG */ | |
168 | 0x3560, /* BOOT LOADER SIZE REG */ | |
169 | 0x3564, /* FW IMAGE ADDR REG */ | |
170 | 0x1000, /* MBX intr enable */ | |
171 | 0x1200, /* Default Intr mask */ | |
172 | 0x1204, /* Default Interrupt ID */ | |
173 | 0x3780, /* QLC_83XX_IDC_MAJ_VERSION */ | |
174 | 0x3784, /* QLC_83XX_IDC_DEV_STATE */ | |
175 | 0x3788, /* QLC_83XX_IDC_DRV_PRESENCE */ | |
176 | 0x378C, /* QLC_83XX_IDC_DRV_ACK */ | |
177 | 0x3790, /* QLC_83XX_IDC_CTRL */ | |
178 | 0x3794, /* QLC_83XX_IDC_DRV_AUDIT */ | |
179 | 0x3798, /* QLC_83XX_IDC_MIN_VERSION */ | |
180 | 0x379C, /* QLC_83XX_RECOVER_DRV_LOCK */ | |
181 | 0x37A0, /* QLC_83XX_IDC_PF_0 */ | |
182 | 0x37A4, /* QLC_83XX_IDC_PF_1 */ | |
183 | 0x37A8, /* QLC_83XX_IDC_PF_2 */ | |
184 | 0x37AC, /* QLC_83XX_IDC_PF_3 */ | |
185 | 0x37B0, /* QLC_83XX_IDC_PF_4 */ | |
186 | 0x37B4, /* QLC_83XX_IDC_PF_5 */ | |
187 | 0x37B8, /* QLC_83XX_IDC_PF_6 */ | |
188 | 0x37BC, /* QLC_83XX_IDC_PF_7 */ | |
189 | 0x37C0, /* QLC_83XX_IDC_PF_8 */ | |
190 | 0x37C4, /* QLC_83XX_IDC_PF_9 */ | |
191 | 0x37C8, /* QLC_83XX_IDC_PF_10 */ | |
192 | 0x37CC, /* QLC_83XX_IDC_PF_11 */ | |
193 | 0x37D0, /* QLC_83XX_IDC_PF_12 */ | |
194 | 0x37D4, /* QLC_83XX_IDC_PF_13 */ | |
195 | 0x37D8, /* QLC_83XX_IDC_PF_14 */ | |
196 | 0x37DC, /* QLC_83XX_IDC_PF_15 */ | |
197 | 0x37E0, /* QLC_83XX_IDC_DEV_PARTITION_INFO_1 */ | |
198 | 0x37E4, /* QLC_83XX_IDC_DEV_PARTITION_INFO_2 */ | |
199 | 0x37F0, /* QLC_83XX_DRV_OP_MODE */ | |
200 | 0x37F4, /* QLC_83XX_VNIC_STATE */ | |
201 | 0x3868, /* QLC_83XX_DRV_LOCK */ | |
202 | 0x386C, /* QLC_83XX_DRV_UNLOCK */ | |
203 | 0x3504, /* QLC_83XX_DRV_LOCK_ID */ | |
204 | 0x34A4, /* QLC_83XX_ASIC_TEMP */ | |
205 | }; | |
206 | ||
207 | static const u32 qlcnic_83xx_reg_tbl[] = { | |
208 | 0x34A8, /* PEG_HALT_STAT1 */ | |
209 | 0x34AC, /* PEG_HALT_STAT2 */ | |
210 | 0x34B0, /* FW_HEARTBEAT */ | |
211 | 0x3500, /* FLASH LOCK_ID */ | |
212 | 0x3528, /* FW_CAPABILITIES */ | |
213 | 0x3538, /* Driver active, DRV_REG0 */ | |
214 | 0x3540, /* Device state, DRV_REG1 */ | |
215 | 0x3544, /* Driver state, DRV_REG2 */ | |
216 | 0x3548, /* Driver scratch, DRV_REG3 */ | |
217 | 0x354C, /* Device partiton info, DRV_REG4 */ | |
218 | 0x3524, /* Driver IDC ver, DRV_REG5 */ | |
219 | 0x3550, /* FW_VER_MAJOR */ | |
220 | 0x3554, /* FW_VER_MINOR */ | |
221 | 0x3558, /* FW_VER_SUB */ | |
222 | 0x359C, /* NPAR STATE */ | |
223 | 0x35FC, /* FW_IMG_VALID */ | |
224 | 0x3650, /* CMD_PEG_STATE */ | |
225 | 0x373C, /* RCV_PEG_STATE */ | |
226 | 0x37B4, /* ASIC TEMP */ | |
227 | 0x356C, /* FW API */ | |
228 | 0x3570, /* DRV OP MODE */ | |
229 | 0x3850, /* FLASH LOCK */ | |
230 | 0x3854, /* FLASH UNLOCK */ | |
231 | }; | |
232 | ||
233 | static struct qlcnic_hardware_ops qlcnic_83xx_hw_ops = { | |
234 | .read_crb = qlcnic_83xx_read_crb, | |
235 | .write_crb = qlcnic_83xx_write_crb, | |
236 | .read_reg = qlcnic_83xx_rd_reg_indirect, | |
237 | .write_reg = qlcnic_83xx_wrt_reg_indirect, | |
238 | .get_mac_address = qlcnic_83xx_get_mac_address, | |
239 | .setup_intr = qlcnic_83xx_setup_intr, | |
240 | .alloc_mbx_args = qlcnic_83xx_alloc_mbx_args, | |
241 | .mbx_cmd = qlcnic_83xx_mbx_op, | |
242 | .get_func_no = qlcnic_83xx_get_func_no, | |
243 | .api_lock = qlcnic_83xx_cam_lock, | |
244 | .api_unlock = qlcnic_83xx_cam_unlock, | |
4be41e92 | 245 | .process_lb_rcv_ring_diag = qlcnic_83xx_process_rcv_ring_diag, |
7f966452 SC |
246 | .create_rx_ctx = qlcnic_83xx_create_rx_ctx, |
247 | .create_tx_ctx = qlcnic_83xx_create_tx_ctx, | |
248 | .setup_link_event = qlcnic_83xx_setup_link_event, | |
249 | .get_nic_info = qlcnic_83xx_get_nic_info, | |
250 | .get_pci_info = qlcnic_83xx_get_pci_info, | |
251 | .set_nic_info = qlcnic_83xx_set_nic_info, | |
252 | .change_macvlan = qlcnic_83xx_sre_macaddr_change, | |
4be41e92 SC |
253 | .napi_enable = qlcnic_83xx_napi_enable, |
254 | .napi_disable = qlcnic_83xx_napi_disable, | |
7f966452 SC |
255 | .config_intr_coal = qlcnic_83xx_config_intr_coal, |
256 | .config_rss = qlcnic_83xx_config_rss, | |
257 | .config_hw_lro = qlcnic_83xx_config_hw_lro, | |
258 | .config_loopback = qlcnic_83xx_set_lb_mode, | |
259 | .clear_loopback = qlcnic_83xx_clear_lb_mode, | |
260 | .config_promisc_mode = qlcnic_83xx_nic_set_promisc, | |
261 | .change_l2_filter = qlcnic_83xx_change_l2_filter, | |
262 | .get_board_info = qlcnic_83xx_get_port_info, | |
263 | }; | |
264 | ||
265 | static struct qlcnic_nic_template qlcnic_83xx_ops = { | |
266 | .config_bridged_mode = qlcnic_config_bridged_mode, | |
267 | .config_led = qlcnic_config_led, | |
4be41e92 SC |
268 | .napi_add = qlcnic_83xx_napi_add, |
269 | .napi_del = qlcnic_83xx_napi_del, | |
7f966452 SC |
270 | .config_ipaddr = qlcnic_83xx_config_ipaddr, |
271 | .clear_legacy_intr = qlcnic_83xx_clear_legacy_intr, | |
272 | }; | |
273 | ||
274 | void qlcnic_83xx_register_map(struct qlcnic_hardware_context *ahw) | |
275 | { | |
276 | ahw->hw_ops = &qlcnic_83xx_hw_ops; | |
277 | ahw->reg_tbl = (u32 *)qlcnic_83xx_reg_tbl; | |
278 | ahw->ext_reg_tbl = (u32 *)qlcnic_83xx_ext_reg_tbl; | |
279 | } | |
280 | ||
281 | int qlcnic_83xx_get_fw_version(struct qlcnic_adapter *adapter) | |
282 | { | |
283 | u32 fw_major, fw_minor, fw_build; | |
284 | struct pci_dev *pdev = adapter->pdev; | |
285 | ||
286 | fw_major = QLC_SHARED_REG_RD32(adapter, QLCNIC_FW_VERSION_MAJOR); | |
287 | fw_minor = QLC_SHARED_REG_RD32(adapter, QLCNIC_FW_VERSION_MINOR); | |
288 | fw_build = QLC_SHARED_REG_RD32(adapter, QLCNIC_FW_VERSION_SUB); | |
289 | adapter->fw_version = QLCNIC_VERSION_CODE(fw_major, fw_minor, fw_build); | |
290 | ||
291 | dev_info(&pdev->dev, "Driver v%s, firmware version %d.%d.%d\n", | |
292 | QLCNIC_LINUX_VERSIONID, fw_major, fw_minor, fw_build); | |
293 | ||
294 | return adapter->fw_version; | |
295 | } | |
296 | ||
297 | static int __qlcnic_set_win_base(struct qlcnic_adapter *adapter, u32 addr) | |
298 | { | |
299 | void __iomem *base; | |
300 | u32 val; | |
301 | ||
302 | base = adapter->ahw->pci_base0 + | |
303 | QLC_83XX_CRB_WIN_FUNC(adapter->ahw->pci_func); | |
304 | writel(addr, base); | |
305 | val = readl(base); | |
306 | if (val != addr) | |
307 | return -EIO; | |
308 | ||
309 | return 0; | |
310 | } | |
311 | ||
312 | int qlcnic_83xx_rd_reg_indirect(struct qlcnic_adapter *adapter, ulong addr) | |
313 | { | |
314 | int ret; | |
315 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
316 | ||
317 | ret = __qlcnic_set_win_base(adapter, (u32) addr); | |
318 | if (!ret) { | |
319 | return QLCRDX(ahw, QLCNIC_WILDCARD); | |
320 | } else { | |
321 | dev_err(&adapter->pdev->dev, | |
322 | "%s failed, addr = 0x%x\n", __func__, (int)addr); | |
323 | return -EIO; | |
324 | } | |
325 | } | |
326 | ||
327 | int qlcnic_83xx_wrt_reg_indirect(struct qlcnic_adapter *adapter, ulong addr, | |
328 | u32 data) | |
329 | { | |
330 | int err; | |
331 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
332 | ||
333 | err = __qlcnic_set_win_base(adapter, (u32) addr); | |
334 | if (!err) { | |
335 | QLCWRX(ahw, QLCNIC_WILDCARD, data); | |
336 | return 0; | |
337 | } else { | |
338 | dev_err(&adapter->pdev->dev, | |
339 | "%s failed, addr = 0x%x data = 0x%x\n", | |
340 | __func__, (int)addr, data); | |
341 | return err; | |
342 | } | |
343 | } | |
344 | ||
345 | int qlcnic_83xx_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr) | |
346 | { | |
347 | int err, i, num_msix; | |
348 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
349 | ||
350 | if (!num_intr) | |
351 | num_intr = QLCNIC_DEF_NUM_STS_DESC_RINGS; | |
352 | num_msix = rounddown_pow_of_two(min_t(int, num_online_cpus(), | |
353 | num_intr)); | |
354 | /* account for AEN interrupt MSI-X based interrupts */ | |
355 | num_msix += 1; | |
356 | num_msix += adapter->max_drv_tx_rings; | |
357 | err = qlcnic_enable_msix(adapter, num_msix); | |
358 | if (err == -ENOMEM) | |
359 | return err; | |
360 | if (adapter->flags & QLCNIC_MSIX_ENABLED) | |
361 | num_msix = adapter->ahw->num_msix; | |
362 | else | |
363 | num_msix = 1; | |
364 | /* setup interrupt mapping table for fw */ | |
365 | ahw->intr_tbl = vzalloc(num_msix * | |
366 | sizeof(struct qlcnic_intrpt_config)); | |
367 | if (!ahw->intr_tbl) | |
368 | return -ENOMEM; | |
369 | if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) { | |
370 | /* MSI-X enablement failed, use legacy interrupt */ | |
371 | adapter->tgt_status_reg = ahw->pci_base0 + QLC_83XX_INTX_PTR; | |
372 | adapter->tgt_mask_reg = ahw->pci_base0 + QLC_83XX_INTX_MASK; | |
373 | adapter->isr_int_vec = ahw->pci_base0 + QLC_83XX_INTX_TRGR; | |
374 | adapter->msix_entries[0].vector = adapter->pdev->irq; | |
375 | dev_info(&adapter->pdev->dev, "using legacy interrupt\n"); | |
376 | } | |
377 | ||
378 | for (i = 0; i < num_msix; i++) { | |
379 | if (adapter->flags & QLCNIC_MSIX_ENABLED) | |
380 | ahw->intr_tbl[i].type = QLCNIC_INTRPT_MSIX; | |
381 | else | |
382 | ahw->intr_tbl[i].type = QLCNIC_INTRPT_INTX; | |
383 | ahw->intr_tbl[i].id = i; | |
384 | ahw->intr_tbl[i].src = 0; | |
385 | } | |
386 | return 0; | |
387 | } | |
388 | ||
389 | inline void qlcnic_83xx_enable_intr(struct qlcnic_adapter *adapter, | |
390 | struct qlcnic_host_sds_ring *sds_ring) | |
391 | { | |
392 | writel(0, sds_ring->crb_intr_mask); | |
393 | if (!QLCNIC_IS_MSI_FAMILY(adapter)) | |
394 | writel(0, adapter->tgt_mask_reg); | |
395 | } | |
396 | ||
397 | static inline void qlcnic_83xx_get_mbx_data(struct qlcnic_adapter *adapter, | |
398 | struct qlcnic_cmd_args *cmd) | |
399 | { | |
400 | int i; | |
401 | for (i = 0; i < cmd->rsp.num; i++) | |
402 | cmd->rsp.arg[i] = readl(QLCNIC_MBX_FW(adapter->ahw, i)); | |
403 | } | |
404 | ||
405 | irqreturn_t qlcnic_83xx_clear_legacy_intr(struct qlcnic_adapter *adapter) | |
406 | { | |
407 | u32 intr_val; | |
408 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
409 | int retries = 0; | |
410 | ||
411 | intr_val = readl(adapter->tgt_status_reg); | |
412 | ||
413 | if (!QLC_83XX_VALID_INTX_BIT31(intr_val)) | |
414 | return IRQ_NONE; | |
415 | ||
416 | if (QLC_83XX_INTX_FUNC(intr_val) != adapter->ahw->pci_func) { | |
417 | adapter->stats.spurious_intr++; | |
418 | return IRQ_NONE; | |
419 | } | |
420 | /* clear the interrupt trigger control register */ | |
421 | writel(0, adapter->isr_int_vec); | |
422 | do { | |
423 | intr_val = readl(adapter->tgt_status_reg); | |
424 | if (QLC_83XX_INTX_FUNC(intr_val) != ahw->pci_func) | |
425 | break; | |
426 | retries++; | |
427 | } while (QLC_83XX_VALID_INTX_BIT30(intr_val) && | |
428 | (retries < QLC_83XX_LEGACY_INTX_MAX_RETRY)); | |
429 | ||
430 | if (retries == QLC_83XX_LEGACY_INTX_MAX_RETRY) { | |
431 | dev_info(&adapter->pdev->dev, | |
432 | "Reached maximum retries to clear legacy interrupt\n"); | |
433 | return IRQ_NONE; | |
434 | } | |
435 | ||
436 | mdelay(QLC_83XX_LEGACY_INTX_DELAY); | |
437 | ||
438 | return IRQ_HANDLED; | |
439 | } | |
440 | ||
441 | irqreturn_t qlcnic_83xx_tmp_intr(int irq, void *data) | |
442 | { | |
443 | struct qlcnic_host_sds_ring *sds_ring = data; | |
444 | struct qlcnic_adapter *adapter = sds_ring->adapter; | |
445 | ||
446 | if (adapter->flags & QLCNIC_MSIX_ENABLED) | |
447 | goto done; | |
448 | ||
449 | if (adapter->nic_ops->clear_legacy_intr(adapter) == IRQ_NONE) | |
450 | return IRQ_NONE; | |
451 | ||
452 | done: | |
453 | adapter->ahw->diag_cnt++; | |
454 | qlcnic_83xx_enable_intr(adapter, sds_ring); | |
455 | ||
456 | return IRQ_HANDLED; | |
457 | } | |
458 | ||
459 | void qlcnic_83xx_free_mbx_intr(struct qlcnic_adapter *adapter) | |
460 | { | |
461 | u32 val = 0; | |
462 | u32 num_msix = adapter->ahw->num_msix - 1; | |
463 | ||
464 | val = (num_msix << 8); | |
465 | ||
466 | QLCWRX(adapter->ahw, QLCNIC_MBX_INTR_ENBL, val); | |
467 | if (adapter->flags & QLCNIC_MSIX_ENABLED) | |
468 | free_irq(adapter->msix_entries[num_msix].vector, adapter); | |
469 | } | |
470 | ||
471 | int qlcnic_83xx_setup_mbx_intr(struct qlcnic_adapter *adapter) | |
472 | { | |
473 | irq_handler_t handler; | |
474 | u32 val; | |
475 | char name[32]; | |
476 | int err = 0; | |
477 | unsigned long flags = 0; | |
478 | ||
479 | if (!(adapter->flags & QLCNIC_MSI_ENABLED) && | |
480 | !(adapter->flags & QLCNIC_MSIX_ENABLED)) | |
481 | flags |= IRQF_SHARED; | |
482 | ||
483 | if (adapter->flags & QLCNIC_MSIX_ENABLED) { | |
484 | handler = qlcnic_83xx_handle_aen; | |
485 | val = adapter->msix_entries[adapter->ahw->num_msix - 1].vector; | |
486 | snprintf(name, (IFNAMSIZ + 4), | |
487 | "%s[%s]", adapter->netdev->name, "aen"); | |
488 | err = request_irq(val, handler, flags, name, adapter); | |
489 | if (err) { | |
490 | dev_err(&adapter->pdev->dev, | |
491 | "failed to register MBX interrupt\n"); | |
492 | return err; | |
493 | } | |
494 | } | |
495 | ||
496 | /* Enable mailbox interrupt */ | |
497 | qlcnic_83xx_enable_mbx_intrpt(adapter); | |
498 | if (adapter->flags & QLCNIC_MSIX_ENABLED) | |
499 | err = qlcnic_83xx_config_intrpt(adapter, 1); | |
500 | ||
501 | return err; | |
502 | } | |
503 | ||
504 | void qlcnic_83xx_get_func_no(struct qlcnic_adapter *adapter) | |
505 | { | |
506 | u32 val = QLCRDX(adapter->ahw, QLCNIC_INFORMANT); | |
507 | adapter->ahw->pci_func = val & 0xf; | |
508 | } | |
509 | ||
510 | int qlcnic_83xx_cam_lock(struct qlcnic_adapter *adapter) | |
511 | { | |
512 | void __iomem *addr; | |
513 | u32 val, limit = 0; | |
514 | ||
515 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
516 | ||
517 | addr = ahw->pci_base0 + QLC_83XX_SEM_LOCK_FUNC(ahw->pci_func); | |
518 | do { | |
519 | val = readl(addr); | |
520 | if (val) { | |
521 | /* write the function number to register */ | |
522 | QLC_SHARED_REG_WR32(adapter, QLCNIC_FLASH_LOCK_OWNER, | |
523 | ahw->pci_func); | |
524 | return 0; | |
525 | } | |
526 | usleep_range(1000, 2000); | |
527 | } while (++limit <= QLCNIC_PCIE_SEM_TIMEOUT); | |
528 | ||
529 | return -EIO; | |
530 | } | |
531 | ||
532 | void qlcnic_83xx_cam_unlock(struct qlcnic_adapter *adapter) | |
533 | { | |
534 | void __iomem *addr; | |
535 | u32 val; | |
536 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
537 | ||
538 | addr = ahw->pci_base0 + QLC_83XX_SEM_UNLOCK_FUNC(ahw->pci_func); | |
539 | val = readl(addr); | |
540 | } | |
541 | ||
542 | void qlcnic_83xx_read_crb(struct qlcnic_adapter *adapter, char *buf, | |
543 | loff_t offset, size_t size) | |
544 | { | |
545 | int ret; | |
546 | u32 data; | |
547 | ||
548 | if (qlcnic_api_lock(adapter)) { | |
549 | dev_err(&adapter->pdev->dev, | |
550 | "%s: failed to acquire lock. addr offset 0x%x\n", | |
551 | __func__, (u32)offset); | |
552 | return; | |
553 | } | |
554 | ||
555 | ret = qlcnic_83xx_rd_reg_indirect(adapter, (u32) offset); | |
556 | qlcnic_api_unlock(adapter); | |
557 | ||
558 | if (ret == -EIO) { | |
559 | dev_err(&adapter->pdev->dev, | |
560 | "%s: failed. addr offset 0x%x\n", | |
561 | __func__, (u32)offset); | |
562 | return; | |
563 | } | |
564 | data = ret; | |
565 | memcpy(buf, &data, size); | |
566 | } | |
567 | ||
568 | void qlcnic_83xx_write_crb(struct qlcnic_adapter *adapter, char *buf, | |
569 | loff_t offset, size_t size) | |
570 | { | |
571 | u32 data; | |
572 | ||
573 | memcpy(&data, buf, size); | |
574 | qlcnic_83xx_wrt_reg_indirect(adapter, (u32) offset, data); | |
575 | } | |
576 | ||
577 | int qlcnic_83xx_get_port_info(struct qlcnic_adapter *adapter) | |
578 | { | |
579 | int status; | |
580 | ||
581 | status = qlcnic_83xx_get_port_config(adapter); | |
582 | if (status) { | |
583 | dev_err(&adapter->pdev->dev, | |
584 | "Get Port Info failed\n"); | |
585 | } else { | |
586 | if (QLC_83XX_SFP_10G_CAPABLE(adapter->ahw->port_config)) | |
587 | adapter->ahw->port_type = QLCNIC_XGBE; | |
588 | else | |
589 | adapter->ahw->port_type = QLCNIC_GBE; | |
590 | if (QLC_83XX_AUTONEG(adapter->ahw->port_config)) | |
591 | adapter->ahw->link_autoneg = AUTONEG_ENABLE; | |
592 | } | |
593 | return status; | |
594 | } | |
595 | ||
596 | void qlcnic_83xx_enable_mbx_intrpt(struct qlcnic_adapter *adapter) | |
597 | { | |
598 | u32 val; | |
599 | ||
600 | if (adapter->flags & QLCNIC_MSIX_ENABLED) | |
601 | val = BIT_2 | ((adapter->ahw->num_msix - 1) << 8); | |
602 | else | |
603 | val = BIT_2; | |
604 | QLCWRX(adapter->ahw, QLCNIC_MBX_INTR_ENBL, val); | |
605 | } | |
606 | ||
607 | void qlcnic_83xx_check_vf(struct qlcnic_adapter *adapter, | |
608 | const struct pci_device_id *ent) | |
609 | { | |
610 | u32 op_mode, priv_level; | |
611 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
612 | ||
613 | /* Determine FW API version */ | |
614 | ahw->fw_hal_version = 2; | |
615 | /* Find PCI function number */ | |
616 | qlcnic_get_func_no(adapter); | |
617 | ||
618 | /* Determine function privilege level */ | |
619 | op_mode = QLCRDX(adapter->ahw, QLC_83XX_DRV_OP_MODE); | |
620 | if (op_mode == QLC_83XX_DEFAULT_OPMODE) | |
621 | priv_level = QLCNIC_MGMT_FUNC; | |
622 | else | |
623 | priv_level = QLC_83XX_GET_FUNC_PRIVILEGE(op_mode, | |
624 | ahw->pci_func); | |
625 | ||
626 | if (priv_level == QLCNIC_NON_PRIV_FUNC) { | |
627 | ahw->op_mode = QLCNIC_NON_PRIV_FUNC; | |
628 | dev_info(&adapter->pdev->dev, | |
629 | "HAL Version: %d Non Privileged function\n", | |
630 | ahw->fw_hal_version); | |
631 | adapter->nic_ops = &qlcnic_vf_ops; | |
632 | } else { | |
633 | adapter->nic_ops = &qlcnic_83xx_ops; | |
634 | } | |
635 | } | |
636 | ||
637 | static void qlcnic_83xx_handle_link_aen(struct qlcnic_adapter *adapter, | |
638 | u32 data[]); | |
639 | static void qlcnic_83xx_handle_idc_comp_aen(struct qlcnic_adapter *adapter, | |
640 | u32 data[]); | |
641 | ||
642 | static void qlcnic_dump_mbx(struct qlcnic_adapter *adapter, | |
643 | struct qlcnic_cmd_args *cmd) | |
644 | { | |
645 | int i; | |
646 | ||
647 | dev_info(&adapter->pdev->dev, | |
648 | "Host MBX regs(%d)\n", cmd->req.num); | |
649 | for (i = 0; i < cmd->req.num; i++) { | |
650 | if (i && !(i % 8)) | |
651 | pr_info("\n"); | |
652 | pr_info("%08x ", cmd->req.arg[i]); | |
653 | } | |
654 | pr_info("\n"); | |
655 | dev_info(&adapter->pdev->dev, | |
656 | "FW MBX regs(%d)\n", cmd->rsp.num); | |
657 | for (i = 0; i < cmd->rsp.num; i++) { | |
658 | if (i && !(i % 8)) | |
659 | pr_info("\n"); | |
660 | pr_info("%08x ", cmd->rsp.arg[i]); | |
661 | } | |
662 | pr_info("\n"); | |
663 | } | |
664 | ||
665 | static u32 qlcnic_83xx_mbx_poll(struct qlcnic_adapter *adapter) | |
666 | { | |
667 | u32 data; | |
668 | unsigned long wait_time = 0; | |
669 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
670 | /* wait for mailbox completion */ | |
671 | do { | |
672 | data = QLCRDX(ahw, QLCNIC_FW_MBX_CTRL); | |
673 | if (++wait_time > QLCNIC_MBX_TIMEOUT) { | |
674 | data = QLCNIC_RCODE_TIMEOUT; | |
675 | break; | |
676 | } | |
677 | mdelay(1); | |
678 | } while (!data); | |
679 | return data; | |
680 | } | |
681 | ||
682 | int qlcnic_83xx_mbx_op(struct qlcnic_adapter *adapter, | |
683 | struct qlcnic_cmd_args *cmd) | |
684 | { | |
685 | int i; | |
686 | u16 opcode; | |
687 | u8 mbx_err_code, mac_cmd_rcode; | |
688 | u32 rsp, mbx_val, fw_data, rsp_num, mbx_cmd, temp, fw[8]; | |
689 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
690 | ||
691 | opcode = LSW(cmd->req.arg[0]); | |
692 | spin_lock(&ahw->mbx_lock); | |
693 | mbx_val = QLCRDX(ahw, QLCNIC_HOST_MBX_CTRL); | |
694 | ||
695 | if (mbx_val) { | |
696 | QLCDB(adapter, DRV, | |
697 | "Mailbox cmd attempted, 0x%x\n", opcode); | |
698 | QLCDB(adapter, DRV, | |
699 | "Mailbox not available, 0x%x, collect FW dump\n", | |
700 | mbx_val); | |
701 | cmd->rsp.arg[0] = QLCNIC_RCODE_TIMEOUT; | |
702 | spin_unlock(&ahw->mbx_lock); | |
703 | return cmd->rsp.arg[0]; | |
704 | } | |
705 | ||
706 | /* Fill in mailbox registers */ | |
707 | mbx_cmd = cmd->req.arg[0]; | |
708 | writel(mbx_cmd, QLCNIC_MBX_HOST(ahw, 0)); | |
709 | for (i = 1; i < cmd->req.num; i++) | |
710 | writel(cmd->req.arg[i], QLCNIC_MBX_HOST(ahw, i)); | |
711 | ||
712 | /* Signal FW about the impending command */ | |
713 | QLCWRX(ahw, QLCNIC_HOST_MBX_CTRL, QLCNIC_SET_OWNER); | |
714 | poll: | |
715 | rsp = qlcnic_83xx_mbx_poll(adapter); | |
716 | /* Get the FW response data */ | |
717 | fw_data = readl(QLCNIC_MBX_FW(ahw, 0)); | |
718 | mbx_err_code = QLCNIC_MBX_STATUS(fw_data); | |
719 | rsp_num = QLCNIC_MBX_NUM_REGS(fw_data); | |
720 | opcode = QLCNIC_MBX_RSP(fw_data); | |
721 | ||
722 | if (rsp != QLCNIC_RCODE_TIMEOUT) { | |
723 | if (opcode == QLCNIC_MBX_LINK_EVENT) { | |
724 | for (i = 0; i < rsp_num; i++) { | |
725 | temp = readl(QLCNIC_MBX_FW(ahw, i)); | |
726 | fw[i] = temp; | |
727 | } | |
728 | qlcnic_83xx_handle_link_aen(adapter, fw); | |
729 | /* clear fw mbx control register */ | |
730 | QLCWRX(ahw, QLCNIC_FW_MBX_CTRL, QLCNIC_CLR_OWNER); | |
731 | mbx_val = QLCRDX(ahw, QLCNIC_HOST_MBX_CTRL); | |
732 | if (mbx_val) | |
733 | goto poll; | |
734 | } else if (opcode == QLCNIC_MBX_COMP_EVENT) { | |
735 | for (i = 0; i < rsp_num; i++) { | |
736 | temp = readl(QLCNIC_MBX_FW(ahw, i)); | |
737 | fw[i] = temp; | |
738 | } | |
739 | qlcnic_83xx_handle_idc_comp_aen(adapter, fw); | |
740 | /* clear fw mbx control register */ | |
741 | QLCWRX(ahw, QLCNIC_FW_MBX_CTRL, QLCNIC_CLR_OWNER); | |
742 | mbx_val = QLCRDX(ahw, QLCNIC_HOST_MBX_CTRL); | |
743 | if (mbx_val) | |
744 | goto poll; | |
745 | } else if (opcode == QLCNIC_MBX_REQUEST_EVENT) { | |
746 | /* IDC Request Notification */ | |
747 | for (i = 0; i < rsp_num; i++) { | |
748 | temp = readl(QLCNIC_MBX_FW(ahw, i)); | |
749 | fw[i] = temp; | |
750 | } | |
751 | for (i = 0; i < QLC_83XX_MBX_AEN_CNT; i++) { | |
752 | temp = QLCNIC_MBX_RSP(fw[i]); | |
753 | adapter->ahw->mbox_aen[i] = temp; | |
754 | } | |
755 | queue_delayed_work(adapter->qlcnic_wq, | |
756 | &adapter->idc_aen_work, 0); | |
757 | /* clear fw mbx control register */ | |
758 | QLCWRX(ahw, QLCNIC_FW_MBX_CTRL, QLCNIC_CLR_OWNER); | |
759 | mbx_val = QLCRDX(ahw, QLCNIC_HOST_MBX_CTRL); | |
760 | if (mbx_val) | |
761 | goto poll; | |
762 | } else if ((mbx_err_code == QLCNIC_MBX_RSP_OK) || | |
763 | (mbx_err_code == QLCNIC_MBX_PORT_RSP_OK)) { | |
764 | qlcnic_83xx_get_mbx_data(adapter, cmd); | |
765 | rsp = QLCNIC_RCODE_SUCCESS; | |
766 | } else { | |
767 | qlcnic_83xx_get_mbx_data(adapter, cmd); | |
768 | if (opcode == QLCNIC_CMD_CONFIG_MAC_VLAN) { | |
769 | fw_data = readl(QLCNIC_MBX_FW(ahw, 2)); | |
770 | mac_cmd_rcode = (u8)fw_data; | |
771 | if (mac_cmd_rcode == QLC_83XX_NO_NIC_RESOURCE || | |
772 | mac_cmd_rcode == QLC_83XX_MAC_PRESENT || | |
773 | mac_cmd_rcode == QLC_83XX_MAC_ABSENT) { | |
774 | rsp = QLCNIC_RCODE_SUCCESS; | |
775 | goto out; | |
776 | } | |
777 | } | |
778 | dev_info(&adapter->pdev->dev, | |
779 | "MBX command 0x%x failed with err:0x%x\n", | |
780 | opcode, mbx_err_code); | |
781 | rsp = mbx_err_code; | |
782 | qlcnic_dump_mbx(adapter, cmd); | |
783 | } | |
784 | } else { | |
785 | dev_info(&adapter->pdev->dev, | |
786 | "MBX command 0x%x timed out\n", opcode); | |
787 | qlcnic_dump_mbx(adapter, cmd); | |
788 | } | |
789 | out: | |
790 | /* clear fw mbx control register */ | |
791 | QLCWRX(ahw, QLCNIC_FW_MBX_CTRL, QLCNIC_CLR_OWNER); | |
792 | spin_unlock(&ahw->mbx_lock); | |
793 | return rsp; | |
794 | } | |
795 | ||
796 | int qlcnic_83xx_alloc_mbx_args(struct qlcnic_cmd_args *mbx, | |
797 | struct qlcnic_adapter *adapter, u32 type) | |
798 | { | |
799 | int i, size; | |
800 | u32 temp; | |
801 | const struct qlcnic_mailbox_metadata *mbx_tbl; | |
802 | ||
803 | mbx_tbl = qlcnic_83xx_mbx_tbl; | |
804 | size = ARRAY_SIZE(qlcnic_83xx_mbx_tbl); | |
805 | for (i = 0; i < size; i++) { | |
806 | if (type == mbx_tbl[i].cmd) { | |
807 | mbx->req.num = mbx_tbl[i].in_args; | |
808 | mbx->rsp.num = mbx_tbl[i].out_args; | |
809 | mbx->req.arg = kcalloc(mbx->req.num, sizeof(u32), | |
810 | GFP_ATOMIC); | |
811 | if (!mbx->req.arg) | |
812 | return -ENOMEM; | |
813 | mbx->rsp.arg = kcalloc(mbx->rsp.num, sizeof(u32), | |
814 | GFP_ATOMIC); | |
815 | if (!mbx->rsp.arg) { | |
816 | kfree(mbx->req.arg); | |
817 | mbx->req.arg = NULL; | |
818 | return -ENOMEM; | |
819 | } | |
820 | memset(mbx->req.arg, 0, sizeof(u32) * mbx->req.num); | |
821 | memset(mbx->rsp.arg, 0, sizeof(u32) * mbx->rsp.num); | |
822 | temp = adapter->ahw->fw_hal_version << 29; | |
823 | mbx->req.arg[0] = (type | (mbx->req.num << 16) | temp); | |
824 | break; | |
825 | } | |
826 | } | |
827 | return 0; | |
828 | } | |
829 | ||
830 | void qlcnic_83xx_idc_aen_work(struct work_struct *work) | |
831 | { | |
832 | struct qlcnic_adapter *adapter; | |
833 | struct qlcnic_cmd_args cmd; | |
834 | int i, err = 0; | |
835 | ||
836 | adapter = container_of(work, struct qlcnic_adapter, idc_aen_work.work); | |
837 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_IDC_ACK); | |
838 | ||
839 | for (i = 1; i < QLC_83XX_MBX_AEN_CNT; i++) | |
840 | cmd.req.arg[i] = adapter->ahw->mbox_aen[i]; | |
841 | ||
842 | err = qlcnic_issue_cmd(adapter, &cmd); | |
843 | if (err) | |
844 | dev_info(&adapter->pdev->dev, | |
845 | "%s: Mailbox IDC ACK failed.\n", __func__); | |
846 | qlcnic_free_mbx_args(&cmd); | |
847 | } | |
848 | ||
849 | static void qlcnic_83xx_handle_idc_comp_aen(struct qlcnic_adapter *adapter, | |
850 | u32 data[]) | |
851 | { | |
852 | dev_dbg(&adapter->pdev->dev, "Completion AEN:0x%x.\n", | |
853 | QLCNIC_MBX_RSP(data[0])); | |
854 | return; | |
855 | } | |
856 | ||
857 | void qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter) | |
858 | { | |
859 | u32 mask, resp, event[QLC_83XX_MBX_AEN_CNT]; | |
860 | int i; | |
861 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
862 | ||
863 | if (!spin_trylock(&ahw->mbx_lock)) { | |
864 | mask = QLCRDX(adapter->ahw, QLCNIC_DEF_INT_MASK); | |
865 | writel(0, adapter->ahw->pci_base0 + mask); | |
866 | return; | |
867 | } | |
868 | resp = QLCRDX(ahw, QLCNIC_FW_MBX_CTRL); | |
869 | ||
870 | if (!(resp & QLCNIC_SET_OWNER)) | |
871 | goto out; | |
872 | ||
873 | for (i = 0; i < QLC_83XX_MBX_AEN_CNT; i++) | |
874 | event[i] = readl(QLCNIC_MBX_FW(ahw, i)); | |
875 | ||
876 | switch (QLCNIC_MBX_RSP(event[0])) { | |
877 | ||
878 | case QLCNIC_MBX_LINK_EVENT: | |
879 | qlcnic_83xx_handle_link_aen(adapter, event); | |
880 | break; | |
881 | case QLCNIC_MBX_COMP_EVENT: | |
882 | qlcnic_83xx_handle_idc_comp_aen(adapter, event); | |
883 | break; | |
884 | case QLCNIC_MBX_REQUEST_EVENT: | |
885 | for (i = 0; i < QLC_83XX_MBX_AEN_CNT; i++) | |
886 | adapter->ahw->mbox_aen[i] = QLCNIC_MBX_RSP(event[i]); | |
887 | queue_delayed_work(adapter->qlcnic_wq, | |
888 | &adapter->idc_aen_work, 0); | |
889 | break; | |
890 | case QLCNIC_MBX_TIME_EXTEND_EVENT: | |
891 | break; | |
892 | case QLCNIC_MBX_SFP_INSERT_EVENT: | |
893 | dev_info(&adapter->pdev->dev, "SFP+ Insert AEN:0x%x.\n", | |
894 | QLCNIC_MBX_RSP(event[0])); | |
895 | break; | |
896 | case QLCNIC_MBX_SFP_REMOVE_EVENT: | |
897 | dev_info(&adapter->pdev->dev, "SFP Removed AEN:0x%x.\n", | |
898 | QLCNIC_MBX_RSP(event[0])); | |
899 | break; | |
900 | default: | |
901 | dev_dbg(&adapter->pdev->dev, "Unsupported AEN:0x%x.\n", | |
902 | QLCNIC_MBX_RSP(event[0])); | |
903 | break; | |
904 | } | |
905 | ||
906 | QLCWRX(ahw, QLCNIC_FW_MBX_CTRL, QLCNIC_CLR_OWNER); | |
907 | out: | |
908 | mask = QLCRDX(adapter->ahw, QLCNIC_DEF_INT_MASK); | |
909 | writel(0, adapter->ahw->pci_base0 + mask); | |
910 | spin_unlock(&ahw->mbx_lock); | |
911 | } | |
912 | ||
913 | static int qlcnic_83xx_add_rings(struct qlcnic_adapter *adapter) | |
914 | { | |
915 | int index, i, err, sds_mbx_size; | |
916 | u32 *buf, intrpt_id, intr_mask; | |
917 | u16 context_id; | |
918 | u8 num_sds; | |
919 | struct qlcnic_cmd_args cmd; | |
920 | struct qlcnic_host_sds_ring *sds; | |
921 | struct qlcnic_sds_mbx sds_mbx; | |
922 | struct qlcnic_add_rings_mbx_out *mbx_out; | |
923 | struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; | |
924 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
925 | ||
926 | sds_mbx_size = sizeof(struct qlcnic_sds_mbx); | |
927 | context_id = recv_ctx->context_id; | |
928 | num_sds = (adapter->max_sds_rings - QLCNIC_MAX_RING_SETS); | |
929 | ahw->hw_ops->alloc_mbx_args(&cmd, adapter, | |
930 | QLCNIC_CMD_ADD_RCV_RINGS); | |
931 | cmd.req.arg[1] = 0 | (num_sds << 8) | (context_id << 16); | |
932 | ||
933 | /* set up status rings, mbx 2-81 */ | |
934 | index = 2; | |
935 | for (i = 8; i < adapter->max_sds_rings; i++) { | |
936 | memset(&sds_mbx, 0, sds_mbx_size); | |
937 | sds = &recv_ctx->sds_rings[i]; | |
938 | sds->consumer = 0; | |
939 | memset(sds->desc_head, 0, STATUS_DESC_RINGSIZE(sds)); | |
940 | sds_mbx.phy_addr = sds->phys_addr; | |
941 | sds_mbx.sds_ring_size = sds->num_desc; | |
942 | ||
943 | if (adapter->flags & QLCNIC_MSIX_ENABLED) | |
944 | intrpt_id = ahw->intr_tbl[i].id; | |
945 | else | |
946 | intrpt_id = QLCRDX(ahw, QLCNIC_DEF_INT_ID); | |
947 | ||
948 | if (adapter->ahw->diag_test != QLCNIC_LOOPBACK_TEST) | |
949 | sds_mbx.intrpt_id = intrpt_id; | |
950 | else | |
951 | sds_mbx.intrpt_id = 0xffff; | |
952 | sds_mbx.intrpt_val = 0; | |
953 | buf = &cmd.req.arg[index]; | |
954 | memcpy(buf, &sds_mbx, sds_mbx_size); | |
955 | index += sds_mbx_size / sizeof(u32); | |
956 | } | |
957 | ||
958 | /* send the mailbox command */ | |
959 | err = ahw->hw_ops->mbx_cmd(adapter, &cmd); | |
960 | if (err) { | |
961 | dev_err(&adapter->pdev->dev, | |
962 | "Failed to add rings %d\n", err); | |
963 | goto out; | |
964 | } | |
965 | ||
966 | mbx_out = (struct qlcnic_add_rings_mbx_out *)&cmd.rsp.arg[1]; | |
967 | index = 0; | |
968 | /* status descriptor ring */ | |
969 | for (i = 8; i < adapter->max_sds_rings; i++) { | |
970 | sds = &recv_ctx->sds_rings[i]; | |
971 | sds->crb_sts_consumer = ahw->pci_base0 + | |
972 | mbx_out->host_csmr[index]; | |
973 | if (adapter->flags & QLCNIC_MSIX_ENABLED) | |
974 | intr_mask = ahw->intr_tbl[i].src; | |
975 | else | |
976 | intr_mask = QLCRDX(ahw, QLCNIC_DEF_INT_MASK); | |
977 | ||
978 | sds->crb_intr_mask = ahw->pci_base0 + intr_mask; | |
979 | index++; | |
980 | } | |
981 | out: | |
982 | qlcnic_free_mbx_args(&cmd); | |
983 | return err; | |
984 | } | |
985 | ||
986 | int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter) | |
987 | { | |
988 | int i, err, index, sds_mbx_size, rds_mbx_size; | |
989 | u8 num_sds, num_rds; | |
990 | u32 *buf, intrpt_id, intr_mask, cap = 0; | |
991 | struct qlcnic_host_sds_ring *sds; | |
992 | struct qlcnic_host_rds_ring *rds; | |
993 | struct qlcnic_sds_mbx sds_mbx; | |
994 | struct qlcnic_rds_mbx rds_mbx; | |
995 | struct qlcnic_cmd_args cmd; | |
996 | struct qlcnic_rcv_mbx_out *mbx_out; | |
997 | struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; | |
998 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
999 | num_rds = adapter->max_rds_rings; | |
1000 | ||
1001 | if (adapter->max_sds_rings <= QLCNIC_MAX_RING_SETS) | |
1002 | num_sds = adapter->max_sds_rings; | |
1003 | else | |
1004 | num_sds = QLCNIC_MAX_RING_SETS; | |
1005 | ||
1006 | sds_mbx_size = sizeof(struct qlcnic_sds_mbx); | |
1007 | rds_mbx_size = sizeof(struct qlcnic_rds_mbx); | |
1008 | cap = QLCNIC_CAP0_LEGACY_CONTEXT; | |
1009 | ||
1010 | if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP) | |
1011 | cap |= QLC_83XX_FW_CAP_LRO_MSS; | |
1012 | ||
1013 | /* set mailbox hdr and capabilities */ | |
1014 | qlcnic_alloc_mbx_args(&cmd, adapter, | |
1015 | QLCNIC_CMD_CREATE_RX_CTX); | |
1016 | cmd.req.arg[1] = cap; | |
1017 | cmd.req.arg[5] = 1 | (num_rds << 5) | (num_sds << 8) | | |
1018 | (QLC_83XX_HOST_RDS_MODE_UNIQUE << 16); | |
1019 | /* set up status rings, mbx 8-57/87 */ | |
1020 | index = QLC_83XX_HOST_SDS_MBX_IDX; | |
1021 | for (i = 0; i < num_sds; i++) { | |
1022 | memset(&sds_mbx, 0, sds_mbx_size); | |
1023 | sds = &recv_ctx->sds_rings[i]; | |
1024 | sds->consumer = 0; | |
1025 | memset(sds->desc_head, 0, STATUS_DESC_RINGSIZE(sds)); | |
1026 | sds_mbx.phy_addr = sds->phys_addr; | |
1027 | sds_mbx.sds_ring_size = sds->num_desc; | |
1028 | if (adapter->flags & QLCNIC_MSIX_ENABLED) | |
1029 | intrpt_id = ahw->intr_tbl[i].id; | |
1030 | else | |
1031 | intrpt_id = QLCRDX(ahw, QLCNIC_DEF_INT_ID); | |
1032 | if (adapter->ahw->diag_test != QLCNIC_LOOPBACK_TEST) | |
1033 | sds_mbx.intrpt_id = intrpt_id; | |
1034 | else | |
1035 | sds_mbx.intrpt_id = 0xffff; | |
1036 | sds_mbx.intrpt_val = 0; | |
1037 | buf = &cmd.req.arg[index]; | |
1038 | memcpy(buf, &sds_mbx, sds_mbx_size); | |
1039 | index += sds_mbx_size / sizeof(u32); | |
1040 | } | |
1041 | /* set up receive rings, mbx 88-111/135 */ | |
1042 | index = QLCNIC_HOST_RDS_MBX_IDX; | |
1043 | rds = &recv_ctx->rds_rings[0]; | |
1044 | rds->producer = 0; | |
1045 | memset(&rds_mbx, 0, rds_mbx_size); | |
1046 | rds_mbx.phy_addr_reg = rds->phys_addr; | |
1047 | rds_mbx.reg_ring_sz = rds->dma_size; | |
1048 | rds_mbx.reg_ring_len = rds->num_desc; | |
1049 | /* Jumbo ring */ | |
1050 | rds = &recv_ctx->rds_rings[1]; | |
1051 | rds->producer = 0; | |
1052 | rds_mbx.phy_addr_jmb = rds->phys_addr; | |
1053 | rds_mbx.jmb_ring_sz = rds->dma_size; | |
1054 | rds_mbx.jmb_ring_len = rds->num_desc; | |
1055 | buf = &cmd.req.arg[index]; | |
1056 | memcpy(buf, &rds_mbx, rds_mbx_size); | |
1057 | ||
1058 | /* send the mailbox command */ | |
1059 | err = ahw->hw_ops->mbx_cmd(adapter, &cmd); | |
1060 | if (err) { | |
1061 | dev_err(&adapter->pdev->dev, | |
1062 | "Failed to create Rx ctx in firmware%d\n", err); | |
1063 | goto out; | |
1064 | } | |
1065 | mbx_out = (struct qlcnic_rcv_mbx_out *)&cmd.rsp.arg[1]; | |
1066 | recv_ctx->context_id = mbx_out->ctx_id; | |
1067 | recv_ctx->state = mbx_out->state; | |
1068 | recv_ctx->virt_port = mbx_out->vport_id; | |
1069 | dev_info(&adapter->pdev->dev, "Rx Context[%d] Created, state:0x%x\n", | |
1070 | recv_ctx->context_id, recv_ctx->state); | |
1071 | /* Receive descriptor ring */ | |
1072 | /* Standard ring */ | |
1073 | rds = &recv_ctx->rds_rings[0]; | |
1074 | rds->crb_rcv_producer = ahw->pci_base0 + | |
1075 | mbx_out->host_prod[0].reg_buf; | |
1076 | /* Jumbo ring */ | |
1077 | rds = &recv_ctx->rds_rings[1]; | |
1078 | rds->crb_rcv_producer = ahw->pci_base0 + | |
1079 | mbx_out->host_prod[0].jmb_buf; | |
1080 | /* status descriptor ring */ | |
1081 | for (i = 0; i < num_sds; i++) { | |
1082 | sds = &recv_ctx->sds_rings[i]; | |
1083 | sds->crb_sts_consumer = ahw->pci_base0 + | |
1084 | mbx_out->host_csmr[i]; | |
1085 | if (adapter->flags & QLCNIC_MSIX_ENABLED) | |
1086 | intr_mask = ahw->intr_tbl[i].src; | |
1087 | else | |
1088 | intr_mask = QLCRDX(ahw, QLCNIC_DEF_INT_MASK); | |
1089 | sds->crb_intr_mask = ahw->pci_base0 + intr_mask; | |
1090 | } | |
1091 | ||
1092 | if (adapter->max_sds_rings > QLCNIC_MAX_RING_SETS) | |
1093 | err = qlcnic_83xx_add_rings(adapter); | |
1094 | out: | |
1095 | qlcnic_free_mbx_args(&cmd); | |
1096 | return err; | |
1097 | } | |
1098 | ||
1099 | int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter, | |
1100 | struct qlcnic_host_tx_ring *tx, int ring) | |
1101 | { | |
1102 | int err; | |
1103 | u16 msix_id; | |
1104 | u32 *buf, intr_mask; | |
1105 | struct qlcnic_cmd_args cmd; | |
1106 | struct qlcnic_tx_mbx mbx; | |
1107 | struct qlcnic_tx_mbx_out *mbx_out; | |
1108 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
1109 | ||
1110 | /* Reset host resources */ | |
1111 | tx->producer = 0; | |
1112 | tx->sw_consumer = 0; | |
1113 | *(tx->hw_consumer) = 0; | |
1114 | ||
1115 | memset(&mbx, 0, sizeof(struct qlcnic_tx_mbx)); | |
1116 | ||
1117 | /* setup mailbox inbox registerss */ | |
1118 | mbx.phys_addr = tx->phys_addr; | |
1119 | mbx.cnsmr_index = tx->hw_cons_phys_addr; | |
1120 | mbx.size = tx->num_desc; | |
1121 | if (adapter->flags & QLCNIC_MSIX_ENABLED) | |
1122 | msix_id = ahw->intr_tbl[adapter->max_sds_rings + ring].id; | |
1123 | else | |
1124 | msix_id = QLCRDX(ahw, QLCNIC_DEF_INT_ID); | |
1125 | if (adapter->ahw->diag_test != QLCNIC_LOOPBACK_TEST) | |
1126 | mbx.intr_id = msix_id; | |
1127 | else | |
1128 | mbx.intr_id = 0xffff; | |
1129 | mbx.src = 0; | |
1130 | ||
1131 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX); | |
1132 | cmd.req.arg[1] = QLCNIC_CAP0_LEGACY_CONTEXT; | |
1133 | cmd.req.arg[5] = QLCNIC_MAX_TX_QUEUES; | |
1134 | buf = &cmd.req.arg[6]; | |
1135 | memcpy(buf, &mbx, sizeof(struct qlcnic_tx_mbx)); | |
1136 | /* send the mailbox command*/ | |
1137 | err = qlcnic_issue_cmd(adapter, &cmd); | |
1138 | if (err) { | |
1139 | dev_err(&adapter->pdev->dev, | |
1140 | "Failed to create Tx ctx in firmware 0x%x\n", err); | |
1141 | goto out; | |
1142 | } | |
1143 | mbx_out = (struct qlcnic_tx_mbx_out *)&cmd.rsp.arg[2]; | |
1144 | tx->crb_cmd_producer = ahw->pci_base0 + mbx_out->host_prod; | |
1145 | tx->ctx_id = mbx_out->ctx_id; | |
1146 | if (adapter->flags & QLCNIC_MSIX_ENABLED) { | |
1147 | intr_mask = ahw->intr_tbl[adapter->max_sds_rings + ring].src; | |
1148 | tx->crb_intr_mask = ahw->pci_base0 + intr_mask; | |
1149 | } | |
1150 | dev_info(&adapter->pdev->dev, "Tx Context[0x%x] Created, state:0x%x\n", | |
1151 | tx->ctx_id, mbx_out->state); | |
1152 | out: | |
1153 | qlcnic_free_mbx_args(&cmd); | |
1154 | return err; | |
1155 | } | |
1156 | ||
1157 | void qlcnic_83xx_register_nic_idc_func(struct qlcnic_adapter *adapter, | |
1158 | int enable) | |
1159 | { | |
1160 | struct qlcnic_cmd_args cmd; | |
1161 | int status; | |
1162 | ||
1163 | if (enable) { | |
1164 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INIT_NIC_FUNC); | |
1165 | cmd.req.arg[1] = 1 | BIT_0; | |
1166 | } else { | |
1167 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_STOP_NIC_FUNC); | |
1168 | cmd.req.arg[1] = 0 | BIT_0; | |
1169 | } | |
1170 | status = qlcnic_issue_cmd(adapter, &cmd); | |
1171 | if (status) | |
1172 | dev_err(&adapter->pdev->dev, | |
1173 | "Failed to %s in NIC IDC function event.\n", | |
1174 | (enable ? "register" : "unregister")); | |
1175 | ||
1176 | qlcnic_free_mbx_args(&cmd); | |
1177 | } | |
1178 | ||
1179 | int qlcnic_83xx_set_port_config(struct qlcnic_adapter *adapter) | |
1180 | { | |
1181 | struct qlcnic_cmd_args cmd; | |
1182 | int err; | |
1183 | ||
1184 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_PORT_CONFIG); | |
1185 | cmd.req.arg[1] = adapter->ahw->port_config; | |
1186 | err = qlcnic_issue_cmd(adapter, &cmd); | |
1187 | if (err) | |
1188 | dev_info(&adapter->pdev->dev, "Set Port Config failed.\n"); | |
1189 | qlcnic_free_mbx_args(&cmd); | |
1190 | return err; | |
1191 | } | |
1192 | ||
1193 | int qlcnic_83xx_get_port_config(struct qlcnic_adapter *adapter) | |
1194 | { | |
1195 | struct qlcnic_cmd_args cmd; | |
1196 | int err; | |
1197 | ||
1198 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PORT_CONFIG); | |
1199 | err = qlcnic_issue_cmd(adapter, &cmd); | |
1200 | if (err) | |
1201 | dev_info(&adapter->pdev->dev, "Get Port config failed\n"); | |
1202 | else | |
1203 | adapter->ahw->port_config = cmd.rsp.arg[1]; | |
1204 | qlcnic_free_mbx_args(&cmd); | |
1205 | return err; | |
1206 | } | |
1207 | ||
1208 | int qlcnic_83xx_setup_link_event(struct qlcnic_adapter *adapter, int enable) | |
1209 | { | |
1210 | int err; | |
1211 | u32 temp; | |
1212 | struct qlcnic_cmd_args cmd; | |
1213 | ||
1214 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LINK_EVENT); | |
1215 | temp = adapter->recv_ctx->context_id << 16; | |
1216 | cmd.req.arg[1] = (enable ? 1 : 0) | BIT_8 | temp; | |
1217 | err = qlcnic_issue_cmd(adapter, &cmd); | |
1218 | if (err) | |
1219 | dev_info(&adapter->pdev->dev, | |
1220 | "Setup linkevent mailbox failed\n"); | |
1221 | qlcnic_free_mbx_args(&cmd); | |
1222 | return err; | |
1223 | } | |
1224 | ||
1225 | int qlcnic_83xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode) | |
1226 | { | |
1227 | int err; | |
1228 | u32 temp; | |
1229 | struct qlcnic_cmd_args cmd; | |
1230 | ||
1231 | if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED) | |
1232 | return -EIO; | |
1233 | ||
1234 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_MAC_RX_MODE); | |
1235 | temp = adapter->recv_ctx->context_id << 16; | |
1236 | cmd.req.arg[1] = (mode ? 1 : 0) | temp; | |
1237 | err = qlcnic_issue_cmd(adapter, &cmd); | |
1238 | if (err) | |
1239 | dev_info(&adapter->pdev->dev, | |
1240 | "Promiscous mode config failed\n"); | |
1241 | qlcnic_free_mbx_args(&cmd); | |
1242 | ||
1243 | return err; | |
1244 | } | |
1245 | ||
1246 | int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode) | |
1247 | { | |
1248 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
1249 | int status = 0; | |
1250 | u32 config; | |
1251 | ||
1252 | status = qlcnic_83xx_get_port_config(adapter); | |
1253 | if (status) | |
1254 | return status; | |
1255 | ||
1256 | config = ahw->port_config; | |
1257 | ||
1258 | if (mode == QLCNIC_ILB_MODE) | |
1259 | ahw->port_config |= QLC_83XX_CFG_LOOPBACK_HSS; | |
1260 | if (mode == QLCNIC_ELB_MODE) | |
1261 | ahw->port_config |= QLC_83XX_CFG_LOOPBACK_EXT; | |
1262 | ||
1263 | status = qlcnic_83xx_set_port_config(adapter); | |
1264 | if (status) { | |
1265 | dev_err(&adapter->pdev->dev, | |
1266 | "Failed to Set Loopback Mode = 0x%x.\n", | |
1267 | ahw->port_config); | |
1268 | ahw->port_config = config; | |
1269 | return status; | |
1270 | } | |
1271 | ||
1272 | qlcnic_sre_macaddr_change(adapter, adapter->mac_addr, 0, | |
1273 | QLCNIC_MAC_ADD); | |
1274 | return status; | |
1275 | } | |
1276 | ||
1277 | int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode) | |
1278 | { | |
1279 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
1280 | int status = 0; | |
1281 | u32 config = ahw->port_config; | |
1282 | ||
1283 | if (mode == QLCNIC_ILB_MODE) | |
1284 | ahw->port_config &= ~QLC_83XX_CFG_LOOPBACK_HSS; | |
1285 | if (mode == QLCNIC_ELB_MODE) | |
1286 | ahw->port_config &= ~QLC_83XX_CFG_LOOPBACK_EXT; | |
1287 | ||
1288 | status = qlcnic_83xx_set_port_config(adapter); | |
1289 | if (status) { | |
1290 | dev_err(&adapter->pdev->dev, | |
1291 | "Failed to Clear Loopback Mode = 0x%x.\n", | |
1292 | ahw->port_config); | |
1293 | ahw->port_config = config; | |
1294 | return status; | |
1295 | } | |
1296 | ||
1297 | qlcnic_sre_macaddr_change(adapter, adapter->mac_addr, 0, | |
1298 | QLCNIC_MAC_DEL); | |
1299 | return status; | |
1300 | } | |
1301 | ||
1302 | void qlcnic_83xx_config_ipaddr(struct qlcnic_adapter *adapter, __be32 ip, | |
1303 | int mode) | |
1304 | { | |
1305 | int err; | |
1306 | u32 temp; | |
1307 | struct qlcnic_cmd_args cmd; | |
1308 | ||
1309 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_IP_ADDR); | |
1310 | if (mode == QLCNIC_IP_UP) { | |
1311 | temp = adapter->recv_ctx->context_id << 16; | |
1312 | cmd.req.arg[1] = 1 | temp; | |
1313 | } else { | |
1314 | temp = adapter->recv_ctx->context_id << 16; | |
1315 | cmd.req.arg[1] = 2 | temp; | |
1316 | } | |
1317 | cmd.req.arg[2] = ntohl(ip); | |
1318 | ||
1319 | err = qlcnic_issue_cmd(adapter, &cmd); | |
1320 | if (err != QLCNIC_RCODE_SUCCESS) | |
1321 | dev_err(&adapter->netdev->dev, | |
1322 | "could not notify %s IP 0x%x request\n", | |
1323 | (mode == QLCNIC_IP_UP) ? "Add" : "Remove", ip); | |
1324 | qlcnic_free_mbx_args(&cmd); | |
1325 | } | |
1326 | ||
1327 | int qlcnic_83xx_config_hw_lro(struct qlcnic_adapter *adapter, int mode) | |
1328 | { | |
1329 | int err; | |
1330 | u32 temp, arg1; | |
1331 | struct qlcnic_cmd_args cmd; | |
1332 | ||
1333 | if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED) | |
1334 | return 0; | |
1335 | ||
1336 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_HW_LRO); | |
1337 | temp = adapter->recv_ctx->context_id << 16; | |
1338 | arg1 = (mode ? (BIT_0 | BIT_1 | BIT_3) : 0) | temp; | |
1339 | cmd.req.arg[1] = arg1; | |
1340 | ||
1341 | err = qlcnic_issue_cmd(adapter, &cmd); | |
1342 | if (err) | |
1343 | dev_info(&adapter->pdev->dev, "LRO config failed\n"); | |
1344 | qlcnic_free_mbx_args(&cmd); | |
1345 | ||
1346 | return err; | |
1347 | } | |
1348 | ||
1349 | int qlcnic_83xx_config_rss(struct qlcnic_adapter *adapter, int enable) | |
1350 | { | |
1351 | int err; | |
1352 | u32 word; | |
1353 | struct qlcnic_cmd_args cmd; | |
1354 | const u64 key[] = { 0xbeac01fa6a42b73bULL, 0x8030f20c77cb2da3ULL, | |
1355 | 0xae7b30b4d0ca2bcbULL, 0x43a38fb04167253dULL, | |
1356 | 0x255b0ec26d5a56daULL }; | |
1357 | ||
1358 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_RSS); | |
1359 | ||
1360 | /* | |
1361 | * RSS request: | |
1362 | * bits 3-0: Rsvd | |
1363 | * 5-4: hash_type_ipv4 | |
1364 | * 7-6: hash_type_ipv6 | |
1365 | * 8: enable | |
1366 | * 9: use indirection table | |
1367 | * 16-31: indirection table mask | |
1368 | */ | |
1369 | word = ((u32)(RSS_HASHTYPE_IP_TCP & 0x3) << 4) | | |
1370 | ((u32)(RSS_HASHTYPE_IP_TCP & 0x3) << 6) | | |
1371 | ((u32)(enable & 0x1) << 8) | | |
1372 | ((0x7ULL) << 16); | |
1373 | cmd.req.arg[1] = (adapter->recv_ctx->context_id); | |
1374 | cmd.req.arg[2] = word; | |
1375 | memcpy(&cmd.req.arg[4], key, sizeof(key)); | |
1376 | ||
1377 | err = qlcnic_issue_cmd(adapter, &cmd); | |
1378 | ||
1379 | if (err) | |
1380 | dev_info(&adapter->pdev->dev, "RSS config failed\n"); | |
1381 | qlcnic_free_mbx_args(&cmd); | |
1382 | ||
1383 | return err; | |
1384 | ||
1385 | } | |
1386 | ||
1387 | int qlcnic_83xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr, | |
1388 | __le16 vlan_id, u8 op) | |
1389 | { | |
1390 | int err; | |
1391 | u32 *buf; | |
1392 | struct qlcnic_cmd_args cmd; | |
1393 | struct qlcnic_macvlan_mbx mv; | |
1394 | ||
1395 | if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED) | |
1396 | return -EIO; | |
1397 | ||
1398 | err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_MAC_VLAN); | |
1399 | if (err) | |
1400 | return err; | |
1401 | cmd.req.arg[1] = op | (1 << 8) | | |
1402 | (adapter->recv_ctx->context_id << 16); | |
1403 | ||
1404 | mv.vlan = le16_to_cpu(vlan_id); | |
1405 | memcpy(&mv.mac, addr, ETH_ALEN); | |
1406 | buf = &cmd.req.arg[2]; | |
1407 | memcpy(buf, &mv, sizeof(struct qlcnic_macvlan_mbx)); | |
1408 | err = qlcnic_issue_cmd(adapter, &cmd); | |
1409 | if (err) | |
1410 | dev_err(&adapter->pdev->dev, | |
1411 | "MAC-VLAN %s to CAM failed, err=%d.\n", | |
1412 | ((op == 1) ? "add " : "delete "), err); | |
1413 | qlcnic_free_mbx_args(&cmd); | |
1414 | return err; | |
1415 | } | |
1416 | ||
1417 | void qlcnic_83xx_change_l2_filter(struct qlcnic_adapter *adapter, u64 *addr, | |
1418 | __le16 vlan_id) | |
1419 | { | |
1420 | u8 mac[ETH_ALEN]; | |
1421 | memcpy(&mac, addr, ETH_ALEN); | |
1422 | qlcnic_83xx_sre_macaddr_change(adapter, mac, vlan_id, QLCNIC_MAC_ADD); | |
1423 | } | |
1424 | ||
1425 | void qlcnic_83xx_configure_mac(struct qlcnic_adapter *adapter, u8 *mac, | |
1426 | u8 type, struct qlcnic_cmd_args *cmd) | |
1427 | { | |
1428 | switch (type) { | |
1429 | case QLCNIC_SET_STATION_MAC: | |
1430 | case QLCNIC_SET_FAC_DEF_MAC: | |
1431 | memcpy(&cmd->req.arg[2], mac, sizeof(u32)); | |
1432 | memcpy(&cmd->req.arg[3], &mac[4], sizeof(u16)); | |
1433 | break; | |
1434 | } | |
1435 | cmd->req.arg[1] = type; | |
1436 | } | |
1437 | ||
1438 | int qlcnic_83xx_get_mac_address(struct qlcnic_adapter *adapter, u8 *mac) | |
1439 | { | |
1440 | int err, i; | |
1441 | struct qlcnic_cmd_args cmd; | |
1442 | u32 mac_low, mac_high; | |
1443 | ||
1444 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_MAC_ADDRESS); | |
1445 | qlcnic_83xx_configure_mac(adapter, mac, QLCNIC_GET_CURRENT_MAC, &cmd); | |
1446 | err = qlcnic_issue_cmd(adapter, &cmd); | |
1447 | ||
1448 | if (err == QLCNIC_RCODE_SUCCESS) { | |
1449 | mac_low = cmd.rsp.arg[1]; | |
1450 | mac_high = cmd.rsp.arg[2]; | |
1451 | ||
1452 | for (i = 0; i < 2; i++) | |
1453 | mac[i] = (u8) (mac_high >> ((1 - i) * 8)); | |
1454 | for (i = 2; i < 6; i++) | |
1455 | mac[i] = (u8) (mac_low >> ((5 - i) * 8)); | |
1456 | } else { | |
1457 | dev_err(&adapter->pdev->dev, "Failed to get mac address%d\n", | |
1458 | err); | |
1459 | err = -EIO; | |
1460 | } | |
1461 | qlcnic_free_mbx_args(&cmd); | |
1462 | return err; | |
1463 | } | |
1464 | ||
1465 | void qlcnic_83xx_config_intr_coal(struct qlcnic_adapter *adapter) | |
1466 | { | |
1467 | int err; | |
1468 | u32 temp; | |
1469 | struct qlcnic_cmd_args cmd; | |
1470 | struct qlcnic_nic_intr_coalesce *coal = &adapter->ahw->coal; | |
1471 | ||
1472 | if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED) | |
1473 | return; | |
1474 | ||
1475 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTR_COAL); | |
1476 | cmd.req.arg[1] = 1 | (adapter->recv_ctx->context_id << 16); | |
1477 | cmd.req.arg[3] = coal->flag; | |
1478 | temp = coal->rx_time_us << 16; | |
1479 | cmd.req.arg[2] = coal->rx_packets | temp; | |
1480 | err = qlcnic_issue_cmd(adapter, &cmd); | |
1481 | if (err != QLCNIC_RCODE_SUCCESS) | |
1482 | dev_info(&adapter->pdev->dev, | |
1483 | "Failed to send interrupt coalescence parameters\n"); | |
1484 | qlcnic_free_mbx_args(&cmd); | |
1485 | } | |
1486 | ||
1487 | static void qlcnic_83xx_handle_link_aen(struct qlcnic_adapter *adapter, | |
1488 | u32 data[]) | |
1489 | { | |
1490 | u8 link_status, duplex; | |
1491 | /* link speed */ | |
1492 | link_status = LSB(data[3]) & 1; | |
1493 | adapter->ahw->link_speed = MSW(data[2]); | |
1494 | adapter->ahw->link_autoneg = MSB(MSW(data[3])); | |
1495 | adapter->ahw->module_type = MSB(LSW(data[3])); | |
1496 | duplex = LSB(MSW(data[3])); | |
1497 | if (duplex) | |
1498 | adapter->ahw->link_duplex = DUPLEX_FULL; | |
1499 | else | |
1500 | adapter->ahw->link_duplex = DUPLEX_HALF; | |
1501 | adapter->ahw->has_link_events = 1; | |
1502 | qlcnic_advert_link_change(adapter, link_status); | |
1503 | } | |
1504 | ||
1505 | irqreturn_t qlcnic_83xx_handle_aen(int irq, void *data) | |
1506 | { | |
1507 | struct qlcnic_adapter *adapter = data; | |
1508 | qlcnic_83xx_process_aen(adapter); | |
1509 | return IRQ_HANDLED; | |
1510 | } | |
1511 | ||
1512 | int qlcnic_enable_eswitch(struct qlcnic_adapter *adapter, u8 port, u8 enable) | |
1513 | { | |
1514 | int err = -EIO; | |
1515 | struct qlcnic_cmd_args cmd; | |
1516 | ||
1517 | if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC) { | |
1518 | dev_err(&adapter->pdev->dev, | |
1519 | "%s: Error, invoked by non management func\n", | |
1520 | __func__); | |
1521 | return err; | |
1522 | } | |
1523 | ||
1524 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_TOGGLE_ESWITCH); | |
1525 | cmd.req.arg[1] = (port & 0xf) | BIT_4; | |
1526 | err = qlcnic_issue_cmd(adapter, &cmd); | |
1527 | ||
1528 | if (err != QLCNIC_RCODE_SUCCESS) { | |
1529 | dev_err(&adapter->pdev->dev, "Failed to enable eswitch%d\n", | |
1530 | err); | |
1531 | err = -EIO; | |
1532 | } | |
1533 | qlcnic_free_mbx_args(&cmd); | |
1534 | ||
1535 | return err; | |
1536 | ||
1537 | } | |
1538 | ||
1539 | int qlcnic_83xx_set_nic_info(struct qlcnic_adapter *adapter, | |
1540 | struct qlcnic_info *nic) | |
1541 | { | |
1542 | int i, err = -EIO; | |
1543 | struct qlcnic_cmd_args cmd; | |
1544 | ||
1545 | if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC) { | |
1546 | dev_err(&adapter->pdev->dev, | |
1547 | "%s: Error, invoked by non management func\n", | |
1548 | __func__); | |
1549 | return err; | |
1550 | } | |
1551 | ||
1552 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO); | |
1553 | cmd.req.arg[1] = (nic->pci_func << 16); | |
1554 | cmd.req.arg[2] = 0x1 << 16; | |
1555 | cmd.req.arg[3] = nic->phys_port | (nic->switch_mode << 16); | |
1556 | cmd.req.arg[4] = nic->capabilities; | |
1557 | cmd.req.arg[5] = (nic->max_mac_filters & 0xFF) | ((nic->max_mtu) << 16); | |
1558 | cmd.req.arg[6] = (nic->max_tx_ques) | ((nic->max_rx_ques) << 16); | |
1559 | cmd.req.arg[7] = (nic->min_tx_bw) | ((nic->max_tx_bw) << 16); | |
1560 | for (i = 8; i < 32; i++) | |
1561 | cmd.req.arg[i] = 0; | |
1562 | ||
1563 | err = qlcnic_issue_cmd(adapter, &cmd); | |
1564 | ||
1565 | if (err != QLCNIC_RCODE_SUCCESS) { | |
1566 | dev_err(&adapter->pdev->dev, "Failed to set nic info%d\n", | |
1567 | err); | |
1568 | err = -EIO; | |
1569 | } | |
1570 | ||
1571 | qlcnic_free_mbx_args(&cmd); | |
1572 | ||
1573 | return err; | |
1574 | } | |
1575 | ||
1576 | int qlcnic_83xx_get_nic_info(struct qlcnic_adapter *adapter, | |
1577 | struct qlcnic_info *npar_info, u8 func_id) | |
1578 | { | |
1579 | int err; | |
1580 | u32 temp; | |
1581 | u8 op = 0; | |
1582 | struct qlcnic_cmd_args cmd; | |
1583 | ||
1584 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO); | |
1585 | if (func_id != adapter->ahw->pci_func) { | |
1586 | temp = func_id << 16; | |
1587 | cmd.req.arg[1] = op | BIT_31 | temp; | |
1588 | } else { | |
1589 | cmd.req.arg[1] = adapter->ahw->pci_func << 16; | |
1590 | } | |
1591 | err = qlcnic_issue_cmd(adapter, &cmd); | |
1592 | if (err) { | |
1593 | dev_info(&adapter->pdev->dev, | |
1594 | "Failed to get nic info %d\n", err); | |
1595 | goto out; | |
1596 | } | |
1597 | ||
1598 | npar_info->op_type = cmd.rsp.arg[1]; | |
1599 | npar_info->pci_func = cmd.rsp.arg[2] & 0xFFFF; | |
1600 | npar_info->op_mode = (cmd.rsp.arg[2] & 0xFFFF0000) >> 16; | |
1601 | npar_info->phys_port = cmd.rsp.arg[3] & 0xFFFF; | |
1602 | npar_info->switch_mode = (cmd.rsp.arg[3] & 0xFFFF0000) >> 16; | |
1603 | npar_info->capabilities = cmd.rsp.arg[4]; | |
1604 | npar_info->max_mac_filters = cmd.rsp.arg[5] & 0xFF; | |
1605 | npar_info->max_mtu = (cmd.rsp.arg[5] & 0xFFFF0000) >> 16; | |
1606 | npar_info->max_tx_ques = cmd.rsp.arg[6] & 0xFFFF; | |
1607 | npar_info->max_rx_ques = (cmd.rsp.arg[6] & 0xFFFF0000) >> 16; | |
1608 | npar_info->min_tx_bw = cmd.rsp.arg[7] & 0xFFFF; | |
1609 | npar_info->max_tx_bw = (cmd.rsp.arg[7] & 0xFFFF0000) >> 16; | |
1610 | if (cmd.rsp.arg[8] & 0x1) | |
1611 | npar_info->max_bw_reg_offset = (cmd.rsp.arg[8] & 0x7FFE) >> 1; | |
1612 | if (cmd.rsp.arg[8] & 0x10000) { | |
1613 | temp = (cmd.rsp.arg[8] & 0x7FFE0000) >> 17; | |
1614 | npar_info->max_linkspeed_reg_offset = temp; | |
1615 | } | |
1616 | ||
1617 | out: | |
1618 | qlcnic_free_mbx_args(&cmd); | |
1619 | return err; | |
1620 | } | |
1621 | ||
1622 | int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *adapter, | |
1623 | struct qlcnic_pci_info *pci_info) | |
1624 | { | |
1625 | int i, err = 0, j = 0; | |
1626 | u32 temp; | |
1627 | struct qlcnic_cmd_args cmd; | |
1628 | ||
1629 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO); | |
1630 | err = qlcnic_issue_cmd(adapter, &cmd); | |
1631 | ||
1632 | adapter->ahw->act_pci_func = 0; | |
1633 | if (err == QLCNIC_RCODE_SUCCESS) { | |
1634 | pci_info->func_count = cmd.rsp.arg[1] & 0xFF; | |
1635 | dev_info(&adapter->pdev->dev, | |
1636 | "%s: total functions = %d\n", | |
1637 | __func__, pci_info->func_count); | |
1638 | for (i = 2, j = 0; j < QLCNIC_MAX_PCI_FUNC; j++, pci_info++) { | |
1639 | pci_info->id = cmd.rsp.arg[i] & 0xFFFF; | |
1640 | pci_info->active = (cmd.rsp.arg[i] & 0xFFFF0000) >> 16; | |
1641 | i++; | |
1642 | pci_info->type = cmd.rsp.arg[i] & 0xFFFF; | |
1643 | if (pci_info->type == QLCNIC_TYPE_NIC) | |
1644 | adapter->ahw->act_pci_func++; | |
1645 | temp = (cmd.rsp.arg[i] & 0xFFFF0000) >> 16; | |
1646 | pci_info->default_port = temp; | |
1647 | i++; | |
1648 | pci_info->tx_min_bw = cmd.rsp.arg[i] & 0xFFFF; | |
1649 | temp = (cmd.rsp.arg[i] & 0xFFFF0000) >> 16; | |
1650 | pci_info->tx_max_bw = temp; | |
1651 | i = i + 2; | |
1652 | memcpy(pci_info->mac, &cmd.rsp.arg[i], ETH_ALEN - 2); | |
1653 | i++; | |
1654 | memcpy(pci_info->mac + sizeof(u32), &cmd.rsp.arg[i], 2); | |
1655 | i = i + 3; | |
1656 | ||
1657 | dev_info(&adapter->pdev->dev, "%s:\n" | |
1658 | "\tid = %d active = %d type = %d\n" | |
1659 | "\tport = %d min bw = %d max bw = %d\n" | |
1660 | "\tmac_addr = %pM\n", __func__, | |
1661 | pci_info->id, pci_info->active, pci_info->type, | |
1662 | pci_info->default_port, pci_info->tx_min_bw, | |
1663 | pci_info->tx_max_bw, pci_info->mac); | |
1664 | } | |
1665 | } else { | |
1666 | dev_err(&adapter->pdev->dev, "Failed to get PCI Info%d\n", | |
1667 | err); | |
1668 | err = -EIO; | |
1669 | } | |
1670 | ||
1671 | qlcnic_free_mbx_args(&cmd); | |
1672 | ||
1673 | return err; | |
1674 | } | |
1675 | ||
1676 | int qlcnic_83xx_config_intrpt(struct qlcnic_adapter *adapter, bool op_type) | |
1677 | { | |
1678 | int i, index, err; | |
1679 | bool type; | |
1680 | u8 max_ints; | |
1681 | u32 val, temp; | |
1682 | struct qlcnic_cmd_args cmd; | |
1683 | ||
1684 | max_ints = adapter->ahw->num_msix; | |
1685 | qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTRPT); | |
1686 | cmd.req.arg[1] = max_ints; | |
1687 | for (i = 0, index = 2; i < max_ints; i++) { | |
1688 | type = op_type ? QLCNIC_INTRPT_ADD : QLCNIC_INTRPT_DEL; | |
1689 | val = type | (adapter->ahw->intr_tbl[i].type << 4); | |
1690 | if (adapter->ahw->intr_tbl[i].type == QLCNIC_INTRPT_MSIX) | |
1691 | val |= (adapter->ahw->intr_tbl[i].id << 16); | |
1692 | cmd.req.arg[index++] = val; | |
1693 | } | |
1694 | err = qlcnic_issue_cmd(adapter, &cmd); | |
1695 | if (err) { | |
1696 | dev_err(&adapter->pdev->dev, | |
1697 | "Failed to configure interrupts 0x%x\n", err); | |
1698 | goto out; | |
1699 | } | |
1700 | ||
1701 | max_ints = cmd.rsp.arg[1]; | |
1702 | for (i = 0, index = 2; i < max_ints; i++, index += 2) { | |
1703 | val = cmd.rsp.arg[index]; | |
1704 | if (LSB(val)) { | |
1705 | dev_info(&adapter->pdev->dev, | |
1706 | "Can't configure interrupt %d\n", | |
1707 | adapter->ahw->intr_tbl[i].id); | |
1708 | continue; | |
1709 | } | |
1710 | if (op_type) { | |
1711 | adapter->ahw->intr_tbl[i].id = MSW(val); | |
1712 | adapter->ahw->intr_tbl[i].enabled = 1; | |
1713 | temp = cmd.rsp.arg[index + 1]; | |
1714 | adapter->ahw->intr_tbl[i].src = temp; | |
1715 | } else { | |
1716 | adapter->ahw->intr_tbl[i].id = i; | |
1717 | adapter->ahw->intr_tbl[i].enabled = 0; | |
1718 | adapter->ahw->intr_tbl[i].src = 0; | |
1719 | } | |
1720 | } | |
1721 | out: | |
1722 | qlcnic_free_mbx_args(&cmd); | |
1723 | return err; | |
1724 | } |