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