Commit | Line | Data |
---|---|---|
577ae39d JK |
1 | /* |
2 | * QLogic qlcnic NIC Driver | |
3 | * Copyright (c) 2009-2013 QLogic Corporation | |
4 | * | |
5 | * See LICENSE.qlcnic for copyright and licensing details. | |
6 | */ | |
7 | ||
ec079a07 | 8 | #include <linux/slab.h> |
ec079a07 SC |
9 | #include <linux/interrupt.h> |
10 | ||
11 | #include "qlcnic.h" | |
319ecf12 | 12 | #include "qlcnic_hw.h" |
ec079a07 SC |
13 | |
14 | #include <linux/swab.h> | |
15 | #include <linux/dma-mapping.h> | |
16 | #include <net/ip.h> | |
17 | #include <linux/ipv6.h> | |
18 | #include <linux/inetdevice.h> | |
19 | #include <linux/sysfs.h> | |
20 | #include <linux/aer.h> | |
21 | #include <linux/log2.h> | |
22 | ||
319ecf12 SC |
23 | #define QLC_STATUS_UNSUPPORTED_CMD -2 |
24 | ||
ec079a07 SC |
25 | int qlcnicvf_config_bridged_mode(struct qlcnic_adapter *adapter, u32 enable) |
26 | { | |
27 | return -EOPNOTSUPP; | |
28 | } | |
29 | ||
30 | int qlcnicvf_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate) | |
31 | { | |
32 | return -EOPNOTSUPP; | |
33 | } | |
34 | ||
b66e29c9 SC |
35 | static ssize_t qlcnic_store_bridged_mode(struct device *dev, |
36 | struct device_attribute *attr, | |
37 | const char *buf, size_t len) | |
ec079a07 SC |
38 | { |
39 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
40 | unsigned long new; | |
41 | int ret = -EINVAL; | |
42 | ||
79788450 | 43 | if (!(adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_BDG)) |
ec079a07 SC |
44 | goto err_out; |
45 | ||
46 | if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) | |
47 | goto err_out; | |
48 | ||
67d6bfa6 | 49 | if (kstrtoul(buf, 2, &new)) |
ec079a07 SC |
50 | goto err_out; |
51 | ||
319ecf12 | 52 | if (!qlcnic_config_bridged_mode(adapter, !!new)) |
ec079a07 SC |
53 | ret = len; |
54 | ||
55 | err_out: | |
56 | return ret; | |
57 | } | |
58 | ||
b66e29c9 SC |
59 | static ssize_t qlcnic_show_bridged_mode(struct device *dev, |
60 | struct device_attribute *attr, | |
61 | char *buf) | |
ec079a07 SC |
62 | { |
63 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
64 | int bridged_mode = 0; | |
65 | ||
79788450 | 66 | if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_BDG) |
ec079a07 SC |
67 | bridged_mode = !!(adapter->flags & QLCNIC_BRIDGE_ENABLED); |
68 | ||
69 | return sprintf(buf, "%d\n", bridged_mode); | |
70 | } | |
71 | ||
b66e29c9 SC |
72 | static ssize_t qlcnic_store_diag_mode(struct device *dev, |
73 | struct device_attribute *attr, | |
74 | const char *buf, size_t len) | |
ec079a07 SC |
75 | { |
76 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
77 | unsigned long new; | |
78 | ||
67d6bfa6 | 79 | if (kstrtoul(buf, 2, &new)) |
ec079a07 SC |
80 | return -EINVAL; |
81 | ||
82 | if (!!new != !!(adapter->flags & QLCNIC_DIAG_ENABLED)) | |
83 | adapter->flags ^= QLCNIC_DIAG_ENABLED; | |
84 | ||
85 | return len; | |
86 | } | |
87 | ||
b66e29c9 SC |
88 | static ssize_t qlcnic_show_diag_mode(struct device *dev, |
89 | struct device_attribute *attr, char *buf) | |
ec079a07 SC |
90 | { |
91 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
319ecf12 | 92 | return sprintf(buf, "%d\n", !!(adapter->flags & QLCNIC_DIAG_ENABLED)); |
ec079a07 SC |
93 | } |
94 | ||
b66e29c9 SC |
95 | static int qlcnic_validate_beacon(struct qlcnic_adapter *adapter, u16 beacon, |
96 | u8 *state, u8 *rate) | |
ec079a07 SC |
97 | { |
98 | *rate = LSB(beacon); | |
99 | *state = MSB(beacon); | |
100 | ||
101 | QLCDB(adapter, DRV, "rate %x state %x\n", *rate, *state); | |
102 | ||
103 | if (!*state) { | |
104 | *rate = __QLCNIC_MAX_LED_RATE; | |
105 | return 0; | |
b66e29c9 | 106 | } else if (*state > __QLCNIC_MAX_LED_STATE) { |
ec079a07 | 107 | return -EINVAL; |
b66e29c9 | 108 | } |
ec079a07 SC |
109 | |
110 | if ((!*rate) || (*rate > __QLCNIC_MAX_LED_RATE)) | |
111 | return -EINVAL; | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
487042af HM |
116 | static int qlcnic_83xx_store_beacon(struct qlcnic_adapter *adapter, |
117 | const char *buf, size_t len) | |
ec079a07 | 118 | { |
319ecf12 | 119 | struct qlcnic_hardware_context *ahw = adapter->ahw; |
319ecf12 | 120 | unsigned long h_beacon; |
487042af | 121 | int err; |
ec079a07 | 122 | |
487042af HM |
123 | if (test_bit(__QLCNIC_RESETTING, &adapter->state)) |
124 | return -EIO; | |
ec079a07 | 125 | |
487042af HM |
126 | if (kstrtoul(buf, 2, &h_beacon)) |
127 | return -EINVAL; | |
319ecf12 | 128 | |
a0431589 HM |
129 | qlcnic_get_beacon_state(adapter); |
130 | ||
487042af | 131 | if (ahw->beacon_state == h_beacon) |
319ecf12 | 132 | return len; |
487042af HM |
133 | |
134 | rtnl_lock(); | |
135 | if (!ahw->beacon_state) { | |
136 | if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) { | |
137 | rtnl_unlock(); | |
138 | return -EBUSY; | |
139 | } | |
319ecf12 SC |
140 | } |
141 | ||
487042af HM |
142 | if (h_beacon) |
143 | err = qlcnic_83xx_config_led(adapter, 1, h_beacon); | |
144 | else | |
145 | err = qlcnic_83xx_config_led(adapter, 0, !h_beacon); | |
146 | if (!err) | |
147 | ahw->beacon_state = h_beacon; | |
148 | ||
149 | if (!ahw->beacon_state) | |
150 | clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); | |
151 | ||
152 | rtnl_unlock(); | |
153 | return len; | |
154 | } | |
155 | ||
156 | static int qlcnic_82xx_store_beacon(struct qlcnic_adapter *adapter, | |
157 | const char *buf, size_t len) | |
158 | { | |
159 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
34e8c406 | 160 | int err, drv_sds_rings = adapter->drv_sds_rings; |
487042af | 161 | u16 beacon; |
a0431589 | 162 | u8 b_state, b_rate; |
487042af | 163 | |
ec079a07 SC |
164 | if (len != sizeof(u16)) |
165 | return QL_STATUS_INVALID_PARAM; | |
166 | ||
167 | memcpy(&beacon, buf, sizeof(u16)); | |
168 | err = qlcnic_validate_beacon(adapter, beacon, &b_state, &b_rate); | |
169 | if (err) | |
170 | return err; | |
171 | ||
a0431589 | 172 | qlcnic_get_beacon_state(adapter); |
487042af HM |
173 | |
174 | if (ahw->beacon_state == b_state) | |
ec079a07 SC |
175 | return len; |
176 | ||
177 | rtnl_lock(); | |
487042af | 178 | if (!ahw->beacon_state) { |
ec079a07 SC |
179 | if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) { |
180 | rtnl_unlock(); | |
181 | return -EBUSY; | |
182 | } | |
487042af | 183 | } |
ec079a07 SC |
184 | |
185 | if (test_bit(__QLCNIC_RESETTING, &adapter->state)) { | |
186 | err = -EIO; | |
187 | goto out; | |
188 | } | |
189 | ||
190 | if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) { | |
191 | err = qlcnic_diag_alloc_res(adapter->netdev, QLCNIC_LED_TEST); | |
192 | if (err) | |
193 | goto out; | |
194 | set_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state); | |
195 | } | |
196 | ||
197 | err = qlcnic_config_led(adapter, b_state, b_rate); | |
361cd29c | 198 | if (!err) { |
ec079a07 | 199 | err = len; |
319ecf12 | 200 | ahw->beacon_state = b_state; |
361cd29c | 201 | } |
ec079a07 SC |
202 | |
203 | if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state)) | |
34e8c406 | 204 | qlcnic_diag_free_res(adapter->netdev, drv_sds_rings); |
ec079a07 | 205 | |
487042af HM |
206 | out: |
207 | if (!ahw->beacon_state) | |
ec079a07 SC |
208 | clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); |
209 | rtnl_unlock(); | |
210 | ||
211 | return err; | |
212 | } | |
213 | ||
487042af HM |
214 | static ssize_t qlcnic_store_beacon(struct device *dev, |
215 | struct device_attribute *attr, | |
216 | const char *buf, size_t len) | |
217 | { | |
218 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
219 | int err = 0; | |
220 | ||
221 | if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) { | |
222 | dev_warn(dev, | |
223 | "LED test not supported in non privileged mode\n"); | |
224 | return -EOPNOTSUPP; | |
225 | } | |
226 | ||
227 | if (qlcnic_82xx_check(adapter)) | |
228 | err = qlcnic_82xx_store_beacon(adapter, buf, len); | |
229 | else if (qlcnic_83xx_check(adapter)) | |
230 | err = qlcnic_83xx_store_beacon(adapter, buf, len); | |
231 | else | |
232 | return -EIO; | |
233 | ||
234 | return err; | |
235 | } | |
236 | ||
b66e29c9 SC |
237 | static ssize_t qlcnic_show_beacon(struct device *dev, |
238 | struct device_attribute *attr, char *buf) | |
ec079a07 SC |
239 | { |
240 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
241 | ||
242 | return sprintf(buf, "%d\n", adapter->ahw->beacon_state); | |
243 | } | |
244 | ||
b66e29c9 SC |
245 | static int qlcnic_sysfs_validate_crb(struct qlcnic_adapter *adapter, |
246 | loff_t offset, size_t size) | |
ec079a07 SC |
247 | { |
248 | size_t crb_size = 4; | |
249 | ||
250 | if (!(adapter->flags & QLCNIC_DIAG_ENABLED)) | |
251 | return -EIO; | |
252 | ||
253 | if (offset < QLCNIC_PCI_CRBSPACE) { | |
254 | if (ADDR_IN_RANGE(offset, QLCNIC_PCI_CAMQM, | |
b66e29c9 | 255 | QLCNIC_PCI_CAMQM_END)) |
ec079a07 SC |
256 | crb_size = 8; |
257 | else | |
258 | return -EINVAL; | |
259 | } | |
260 | ||
261 | if ((size != crb_size) || (offset & (crb_size-1))) | |
262 | return -EINVAL; | |
263 | ||
264 | return 0; | |
265 | } | |
266 | ||
b66e29c9 SC |
267 | static ssize_t qlcnic_sysfs_read_crb(struct file *filp, struct kobject *kobj, |
268 | struct bin_attribute *attr, char *buf, | |
269 | loff_t offset, size_t size) | |
ec079a07 SC |
270 | { |
271 | struct device *dev = container_of(kobj, struct device, kobj); | |
272 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
ec079a07 SC |
273 | int ret; |
274 | ||
275 | ret = qlcnic_sysfs_validate_crb(adapter, offset, size); | |
276 | if (ret != 0) | |
277 | return ret; | |
319ecf12 | 278 | qlcnic_read_crb(adapter, buf, offset, size); |
ec079a07 | 279 | |
ec079a07 SC |
280 | return size; |
281 | } | |
282 | ||
b66e29c9 SC |
283 | static ssize_t qlcnic_sysfs_write_crb(struct file *filp, struct kobject *kobj, |
284 | struct bin_attribute *attr, char *buf, | |
285 | loff_t offset, size_t size) | |
ec079a07 SC |
286 | { |
287 | struct device *dev = container_of(kobj, struct device, kobj); | |
288 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
ec079a07 SC |
289 | int ret; |
290 | ||
291 | ret = qlcnic_sysfs_validate_crb(adapter, offset, size); | |
292 | if (ret != 0) | |
293 | return ret; | |
294 | ||
319ecf12 | 295 | qlcnic_write_crb(adapter, buf, offset, size); |
ec079a07 SC |
296 | return size; |
297 | } | |
298 | ||
b66e29c9 SC |
299 | static int qlcnic_sysfs_validate_mem(struct qlcnic_adapter *adapter, |
300 | loff_t offset, size_t size) | |
ec079a07 SC |
301 | { |
302 | if (!(adapter->flags & QLCNIC_DIAG_ENABLED)) | |
303 | return -EIO; | |
304 | ||
305 | if ((size != 8) || (offset & 0x7)) | |
306 | return -EIO; | |
307 | ||
308 | return 0; | |
309 | } | |
310 | ||
b66e29c9 SC |
311 | static ssize_t qlcnic_sysfs_read_mem(struct file *filp, struct kobject *kobj, |
312 | struct bin_attribute *attr, char *buf, | |
313 | loff_t offset, size_t size) | |
ec079a07 SC |
314 | { |
315 | struct device *dev = container_of(kobj, struct device, kobj); | |
316 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
317 | u64 data; | |
318 | int ret; | |
319 | ||
320 | ret = qlcnic_sysfs_validate_mem(adapter, offset, size); | |
321 | if (ret != 0) | |
322 | return ret; | |
323 | ||
324 | if (qlcnic_pci_mem_read_2M(adapter, offset, &data)) | |
325 | return -EIO; | |
326 | ||
327 | memcpy(buf, &data, size); | |
328 | ||
329 | return size; | |
330 | } | |
331 | ||
b66e29c9 SC |
332 | static ssize_t qlcnic_sysfs_write_mem(struct file *filp, struct kobject *kobj, |
333 | struct bin_attribute *attr, char *buf, | |
334 | loff_t offset, size_t size) | |
ec079a07 SC |
335 | { |
336 | struct device *dev = container_of(kobj, struct device, kobj); | |
337 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
338 | u64 data; | |
339 | int ret; | |
340 | ||
341 | ret = qlcnic_sysfs_validate_mem(adapter, offset, size); | |
342 | if (ret != 0) | |
343 | return ret; | |
344 | ||
345 | memcpy(&data, buf, size); | |
346 | ||
347 | if (qlcnic_pci_mem_write_2M(adapter, offset, data)) | |
348 | return -EIO; | |
349 | ||
350 | return size; | |
351 | } | |
352 | ||
2f514c52 | 353 | static u32 qlcnic_get_pci_func_count(struct qlcnic_adapter *adapter) |
319ecf12 | 354 | { |
2f514c52 JK |
355 | struct qlcnic_hardware_context *ahw = adapter->ahw; |
356 | u32 count = 0; | |
357 | ||
358 | if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) | |
359 | return ahw->total_nic_func; | |
360 | ||
361 | if (ahw->total_pci_func <= QLC_DEFAULT_VNIC_COUNT) | |
362 | count = QLC_DEFAULT_VNIC_COUNT; | |
363 | else | |
364 | count = ahw->max_vnic_func; | |
365 | ||
366 | return count; | |
367 | } | |
368 | ||
369 | int qlcnic_is_valid_nic_func(struct qlcnic_adapter *adapter, u8 pci_func) | |
370 | { | |
371 | u32 pci_func_count = qlcnic_get_pci_func_count(adapter); | |
319ecf12 | 372 | int i; |
2f514c52 JK |
373 | |
374 | for (i = 0; i < pci_func_count; i++) { | |
319ecf12 SC |
375 | if (adapter->npars[i].pci_func == pci_func) |
376 | return i; | |
377 | } | |
378 | ||
379 | return -1; | |
380 | } | |
381 | ||
b66e29c9 SC |
382 | static int validate_pm_config(struct qlcnic_adapter *adapter, |
383 | struct qlcnic_pm_func_cfg *pm_cfg, int count) | |
ec079a07 | 384 | { |
319ecf12 SC |
385 | u8 src_pci_func, s_esw_id, d_esw_id; |
386 | u8 dest_pci_func; | |
387 | int i, src_index, dest_index; | |
ec079a07 SC |
388 | |
389 | for (i = 0; i < count; i++) { | |
390 | src_pci_func = pm_cfg[i].pci_func; | |
391 | dest_pci_func = pm_cfg[i].dest_npar; | |
319ecf12 | 392 | src_index = qlcnic_is_valid_nic_func(adapter, src_pci_func); |
319ecf12 | 393 | if (src_index < 0) |
ec079a07 SC |
394 | return QL_STATUS_INVALID_PARAM; |
395 | ||
319ecf12 SC |
396 | dest_index = qlcnic_is_valid_nic_func(adapter, dest_pci_func); |
397 | if (dest_index < 0) | |
ec079a07 SC |
398 | return QL_STATUS_INVALID_PARAM; |
399 | ||
319ecf12 SC |
400 | s_esw_id = adapter->npars[src_index].phy_port; |
401 | d_esw_id = adapter->npars[dest_index].phy_port; | |
ec079a07 SC |
402 | |
403 | if (s_esw_id != d_esw_id) | |
404 | return QL_STATUS_INVALID_PARAM; | |
ec079a07 | 405 | } |
ec079a07 | 406 | |
319ecf12 | 407 | return 0; |
ec079a07 SC |
408 | } |
409 | ||
b66e29c9 SC |
410 | static ssize_t qlcnic_sysfs_write_pm_config(struct file *filp, |
411 | struct kobject *kobj, | |
412 | struct bin_attribute *attr, | |
413 | char *buf, loff_t offset, | |
414 | size_t size) | |
ec079a07 SC |
415 | { |
416 | struct device *dev = container_of(kobj, struct device, kobj); | |
417 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
418 | struct qlcnic_pm_func_cfg *pm_cfg; | |
419 | u32 id, action, pci_func; | |
319ecf12 | 420 | int count, rem, i, ret, index; |
ec079a07 SC |
421 | |
422 | count = size / sizeof(struct qlcnic_pm_func_cfg); | |
423 | rem = size % sizeof(struct qlcnic_pm_func_cfg); | |
424 | if (rem) | |
425 | return QL_STATUS_INVALID_PARAM; | |
426 | ||
b66e29c9 | 427 | pm_cfg = (struct qlcnic_pm_func_cfg *)buf; |
ec079a07 | 428 | ret = validate_pm_config(adapter, pm_cfg, count); |
319ecf12 | 429 | |
ec079a07 SC |
430 | if (ret) |
431 | return ret; | |
432 | for (i = 0; i < count; i++) { | |
433 | pci_func = pm_cfg[i].pci_func; | |
434 | action = !!pm_cfg[i].action; | |
319ecf12 SC |
435 | index = qlcnic_is_valid_nic_func(adapter, pci_func); |
436 | if (index < 0) | |
437 | return QL_STATUS_INVALID_PARAM; | |
438 | ||
439 | id = adapter->npars[index].phy_port; | |
440 | ret = qlcnic_config_port_mirroring(adapter, id, | |
441 | action, pci_func); | |
ec079a07 SC |
442 | if (ret) |
443 | return ret; | |
444 | } | |
445 | ||
446 | for (i = 0; i < count; i++) { | |
447 | pci_func = pm_cfg[i].pci_func; | |
319ecf12 | 448 | index = qlcnic_is_valid_nic_func(adapter, pci_func); |
2f514c52 JK |
449 | if (index < 0) |
450 | return QL_STATUS_INVALID_PARAM; | |
319ecf12 SC |
451 | id = adapter->npars[index].phy_port; |
452 | adapter->npars[index].enable_pm = !!pm_cfg[i].action; | |
453 | adapter->npars[index].dest_npar = id; | |
ec079a07 | 454 | } |
319ecf12 | 455 | |
ec079a07 SC |
456 | return size; |
457 | } | |
458 | ||
b66e29c9 SC |
459 | static ssize_t qlcnic_sysfs_read_pm_config(struct file *filp, |
460 | struct kobject *kobj, | |
461 | struct bin_attribute *attr, | |
462 | char *buf, loff_t offset, | |
463 | size_t size) | |
ec079a07 SC |
464 | { |
465 | struct device *dev = container_of(kobj, struct device, kobj); | |
466 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
2f514c52 JK |
467 | u32 pci_func_count = qlcnic_get_pci_func_count(adapter); |
468 | struct qlcnic_pm_func_cfg *pm_cfg; | |
469 | int i, pm_cfg_size; | |
319ecf12 | 470 | u8 pci_func; |
ec079a07 | 471 | |
2f514c52 JK |
472 | pm_cfg_size = pci_func_count * sizeof(*pm_cfg); |
473 | if (size != pm_cfg_size) | |
ec079a07 SC |
474 | return QL_STATUS_INVALID_PARAM; |
475 | ||
2f514c52 JK |
476 | memset(buf, 0, pm_cfg_size); |
477 | pm_cfg = (struct qlcnic_pm_func_cfg *)buf; | |
319ecf12 | 478 | |
2f514c52 | 479 | for (i = 0; i < pci_func_count; i++) { |
319ecf12 | 480 | pci_func = adapter->npars[i].pci_func; |
35dafcb0 SC |
481 | if (!adapter->npars[i].active) |
482 | continue; | |
483 | ||
484 | if (!adapter->npars[i].eswitch_status) | |
485 | continue; | |
486 | ||
319ecf12 SC |
487 | pm_cfg[pci_func].action = adapter->npars[i].enable_pm; |
488 | pm_cfg[pci_func].dest_npar = 0; | |
489 | pm_cfg[pci_func].pci_func = i; | |
ec079a07 | 490 | } |
ec079a07 SC |
491 | return size; |
492 | } | |
493 | ||
b66e29c9 SC |
494 | static int validate_esw_config(struct qlcnic_adapter *adapter, |
495 | struct qlcnic_esw_func_cfg *esw_cfg, int count) | |
ec079a07 | 496 | { |
2f514c52 JK |
497 | u32 pci_func_count = qlcnic_get_pci_func_count(adapter); |
498 | struct qlcnic_hardware_context *ahw = adapter->ahw; | |
499 | int i, ret; | |
ec079a07 SC |
500 | u32 op_mode; |
501 | u8 pci_func; | |
ec079a07 | 502 | |
319ecf12 | 503 | if (qlcnic_82xx_check(adapter)) |
2f514c52 | 504 | op_mode = readl(ahw->pci_base0 + QLCNIC_DRV_OP_MODE); |
319ecf12 | 505 | else |
2f514c52 | 506 | op_mode = QLCRDX(ahw, QLC_83XX_DRV_OP_MODE); |
ec079a07 SC |
507 | |
508 | for (i = 0; i < count; i++) { | |
509 | pci_func = esw_cfg[i].pci_func; | |
2f514c52 | 510 | if (pci_func >= pci_func_count) |
ec079a07 SC |
511 | return QL_STATUS_INVALID_PARAM; |
512 | ||
319ecf12 SC |
513 | if (adapter->ahw->op_mode == QLCNIC_MGMT_FUNC) |
514 | if (qlcnic_is_valid_nic_func(adapter, pci_func) < 0) | |
ec079a07 SC |
515 | return QL_STATUS_INVALID_PARAM; |
516 | ||
517 | switch (esw_cfg[i].op_mode) { | |
518 | case QLCNIC_PORT_DEFAULTS: | |
319ecf12 SC |
519 | if (qlcnic_82xx_check(adapter)) { |
520 | ret = QLC_DEV_GET_DRV(op_mode, pci_func); | |
521 | } else { | |
522 | ret = QLC_83XX_GET_FUNC_PRIVILEGE(op_mode, | |
523 | pci_func); | |
524 | esw_cfg[i].offload_flags = 0; | |
525 | } | |
526 | ||
527 | if (ret != QLCNIC_NON_PRIV_FUNC) { | |
ec079a07 SC |
528 | if (esw_cfg[i].mac_anti_spoof != 0) |
529 | return QL_STATUS_INVALID_PARAM; | |
530 | if (esw_cfg[i].mac_override != 1) | |
531 | return QL_STATUS_INVALID_PARAM; | |
532 | if (esw_cfg[i].promisc_mode != 1) | |
533 | return QL_STATUS_INVALID_PARAM; | |
534 | } | |
535 | break; | |
536 | case QLCNIC_ADD_VLAN: | |
537 | if (!IS_VALID_VLAN(esw_cfg[i].vlan_id)) | |
538 | return QL_STATUS_INVALID_PARAM; | |
539 | if (!esw_cfg[i].op_type) | |
540 | return QL_STATUS_INVALID_PARAM; | |
541 | break; | |
542 | case QLCNIC_DEL_VLAN: | |
543 | if (!esw_cfg[i].op_type) | |
544 | return QL_STATUS_INVALID_PARAM; | |
545 | break; | |
546 | default: | |
547 | return QL_STATUS_INVALID_PARAM; | |
548 | } | |
549 | } | |
319ecf12 | 550 | |
ec079a07 SC |
551 | return 0; |
552 | } | |
553 | ||
b66e29c9 SC |
554 | static ssize_t qlcnic_sysfs_write_esw_config(struct file *file, |
555 | struct kobject *kobj, | |
556 | struct bin_attribute *attr, | |
557 | char *buf, loff_t offset, | |
558 | size_t size) | |
ec079a07 SC |
559 | { |
560 | struct device *dev = container_of(kobj, struct device, kobj); | |
561 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
562 | struct qlcnic_esw_func_cfg *esw_cfg; | |
563 | struct qlcnic_npar_info *npar; | |
564 | int count, rem, i, ret; | |
319ecf12 SC |
565 | int index; |
566 | u8 op_mode = 0, pci_func; | |
ec079a07 SC |
567 | |
568 | count = size / sizeof(struct qlcnic_esw_func_cfg); | |
569 | rem = size % sizeof(struct qlcnic_esw_func_cfg); | |
570 | if (rem) | |
571 | return QL_STATUS_INVALID_PARAM; | |
572 | ||
b66e29c9 | 573 | esw_cfg = (struct qlcnic_esw_func_cfg *)buf; |
ec079a07 SC |
574 | ret = validate_esw_config(adapter, esw_cfg, count); |
575 | if (ret) | |
576 | return ret; | |
577 | ||
578 | for (i = 0; i < count; i++) { | |
319ecf12 | 579 | if (adapter->ahw->op_mode == QLCNIC_MGMT_FUNC) |
ec079a07 SC |
580 | if (qlcnic_config_switch_port(adapter, &esw_cfg[i])) |
581 | return QL_STATUS_INVALID_PARAM; | |
582 | ||
583 | if (adapter->ahw->pci_func != esw_cfg[i].pci_func) | |
584 | continue; | |
585 | ||
586 | op_mode = esw_cfg[i].op_mode; | |
587 | qlcnic_get_eswitch_port_config(adapter, &esw_cfg[i]); | |
588 | esw_cfg[i].op_mode = op_mode; | |
589 | esw_cfg[i].pci_func = adapter->ahw->pci_func; | |
590 | ||
591 | switch (esw_cfg[i].op_mode) { | |
592 | case QLCNIC_PORT_DEFAULTS: | |
593 | qlcnic_set_eswitch_port_features(adapter, &esw_cfg[i]); | |
147a9088 SS |
594 | rtnl_lock(); |
595 | qlcnic_set_netdev_features(adapter, &esw_cfg[i]); | |
596 | rtnl_unlock(); | |
ec079a07 SC |
597 | break; |
598 | case QLCNIC_ADD_VLAN: | |
599 | qlcnic_set_vlan_config(adapter, &esw_cfg[i]); | |
600 | break; | |
601 | case QLCNIC_DEL_VLAN: | |
602 | esw_cfg[i].vlan_id = 0; | |
603 | qlcnic_set_vlan_config(adapter, &esw_cfg[i]); | |
604 | break; | |
605 | } | |
606 | } | |
607 | ||
79788450 | 608 | if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC) |
ec079a07 SC |
609 | goto out; |
610 | ||
611 | for (i = 0; i < count; i++) { | |
612 | pci_func = esw_cfg[i].pci_func; | |
319ecf12 | 613 | index = qlcnic_is_valid_nic_func(adapter, pci_func); |
2f514c52 JK |
614 | if (index < 0) |
615 | return QL_STATUS_INVALID_PARAM; | |
319ecf12 | 616 | npar = &adapter->npars[index]; |
ec079a07 SC |
617 | switch (esw_cfg[i].op_mode) { |
618 | case QLCNIC_PORT_DEFAULTS: | |
619 | npar->promisc_mode = esw_cfg[i].promisc_mode; | |
620 | npar->mac_override = esw_cfg[i].mac_override; | |
621 | npar->offload_flags = esw_cfg[i].offload_flags; | |
622 | npar->mac_anti_spoof = esw_cfg[i].mac_anti_spoof; | |
623 | npar->discard_tagged = esw_cfg[i].discard_tagged; | |
624 | break; | |
625 | case QLCNIC_ADD_VLAN: | |
626 | npar->pvid = esw_cfg[i].vlan_id; | |
627 | break; | |
628 | case QLCNIC_DEL_VLAN: | |
629 | npar->pvid = 0; | |
630 | break; | |
631 | } | |
632 | } | |
633 | out: | |
634 | return size; | |
635 | } | |
636 | ||
b66e29c9 SC |
637 | static ssize_t qlcnic_sysfs_read_esw_config(struct file *file, |
638 | struct kobject *kobj, | |
639 | struct bin_attribute *attr, | |
640 | char *buf, loff_t offset, | |
641 | size_t size) | |
ec079a07 SC |
642 | { |
643 | struct device *dev = container_of(kobj, struct device, kobj); | |
644 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
2f514c52 JK |
645 | u32 pci_func_count = qlcnic_get_pci_func_count(adapter); |
646 | struct qlcnic_esw_func_cfg *esw_cfg; | |
647 | size_t esw_cfg_size; | |
319ecf12 | 648 | u8 i, pci_func; |
ec079a07 | 649 | |
2f514c52 JK |
650 | esw_cfg_size = pci_func_count * sizeof(*esw_cfg); |
651 | if (size != esw_cfg_size) | |
ec079a07 SC |
652 | return QL_STATUS_INVALID_PARAM; |
653 | ||
2f514c52 JK |
654 | memset(buf, 0, esw_cfg_size); |
655 | esw_cfg = (struct qlcnic_esw_func_cfg *)buf; | |
319ecf12 | 656 | |
2f514c52 | 657 | for (i = 0; i < pci_func_count; i++) { |
319ecf12 | 658 | pci_func = adapter->npars[i].pci_func; |
35dafcb0 SC |
659 | if (!adapter->npars[i].active) |
660 | continue; | |
661 | ||
662 | if (!adapter->npars[i].eswitch_status) | |
663 | continue; | |
664 | ||
319ecf12 SC |
665 | esw_cfg[pci_func].pci_func = pci_func; |
666 | if (qlcnic_get_eswitch_port_config(adapter, &esw_cfg[pci_func])) | |
ec079a07 SC |
667 | return QL_STATUS_INVALID_PARAM; |
668 | } | |
ec079a07 SC |
669 | return size; |
670 | } | |
671 | ||
b66e29c9 SC |
672 | static int validate_npar_config(struct qlcnic_adapter *adapter, |
673 | struct qlcnic_npar_func_cfg *np_cfg, | |
674 | int count) | |
ec079a07 SC |
675 | { |
676 | u8 pci_func, i; | |
677 | ||
678 | for (i = 0; i < count; i++) { | |
679 | pci_func = np_cfg[i].pci_func; | |
319ecf12 | 680 | if (qlcnic_is_valid_nic_func(adapter, pci_func) < 0) |
ec079a07 SC |
681 | return QL_STATUS_INVALID_PARAM; |
682 | ||
683 | if (!IS_VALID_BW(np_cfg[i].min_bw) || | |
684 | !IS_VALID_BW(np_cfg[i].max_bw)) | |
685 | return QL_STATUS_INVALID_PARAM; | |
686 | } | |
687 | return 0; | |
688 | } | |
689 | ||
b66e29c9 SC |
690 | static ssize_t qlcnic_sysfs_write_npar_config(struct file *file, |
691 | struct kobject *kobj, | |
692 | struct bin_attribute *attr, | |
693 | char *buf, loff_t offset, | |
694 | size_t size) | |
ec079a07 SC |
695 | { |
696 | struct device *dev = container_of(kobj, struct device, kobj); | |
697 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
698 | struct qlcnic_info nic_info; | |
699 | struct qlcnic_npar_func_cfg *np_cfg; | |
319ecf12 | 700 | int i, count, rem, ret, index; |
ec079a07 SC |
701 | u8 pci_func; |
702 | ||
703 | count = size / sizeof(struct qlcnic_npar_func_cfg); | |
704 | rem = size % sizeof(struct qlcnic_npar_func_cfg); | |
705 | if (rem) | |
706 | return QL_STATUS_INVALID_PARAM; | |
707 | ||
b66e29c9 | 708 | np_cfg = (struct qlcnic_npar_func_cfg *)buf; |
ec079a07 SC |
709 | ret = validate_npar_config(adapter, np_cfg, count); |
710 | if (ret) | |
711 | return ret; | |
712 | ||
319ecf12 | 713 | for (i = 0; i < count; i++) { |
ec079a07 | 714 | pci_func = np_cfg[i].pci_func; |
319ecf12 SC |
715 | |
716 | memset(&nic_info, 0, sizeof(struct qlcnic_info)); | |
ec079a07 SC |
717 | ret = qlcnic_get_nic_info(adapter, &nic_info, pci_func); |
718 | if (ret) | |
719 | return ret; | |
720 | nic_info.pci_func = pci_func; | |
721 | nic_info.min_tx_bw = np_cfg[i].min_bw; | |
722 | nic_info.max_tx_bw = np_cfg[i].max_bw; | |
723 | ret = qlcnic_set_nic_info(adapter, &nic_info); | |
724 | if (ret) | |
725 | return ret; | |
319ecf12 | 726 | index = qlcnic_is_valid_nic_func(adapter, pci_func); |
2f514c52 JK |
727 | if (index < 0) |
728 | return QL_STATUS_INVALID_PARAM; | |
319ecf12 SC |
729 | adapter->npars[index].min_bw = nic_info.min_tx_bw; |
730 | adapter->npars[index].max_bw = nic_info.max_tx_bw; | |
ec079a07 SC |
731 | } |
732 | ||
733 | return size; | |
ec079a07 | 734 | } |
b66e29c9 SC |
735 | |
736 | static ssize_t qlcnic_sysfs_read_npar_config(struct file *file, | |
737 | struct kobject *kobj, | |
738 | struct bin_attribute *attr, | |
739 | char *buf, loff_t offset, | |
740 | size_t size) | |
ec079a07 SC |
741 | { |
742 | struct device *dev = container_of(kobj, struct device, kobj); | |
743 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
2f514c52 JK |
744 | u32 pci_func_count = qlcnic_get_pci_func_count(adapter); |
745 | struct qlcnic_npar_func_cfg *np_cfg; | |
ec079a07 | 746 | struct qlcnic_info nic_info; |
2f514c52 | 747 | size_t np_cfg_size; |
ec079a07 SC |
748 | int i, ret; |
749 | ||
2f514c52 JK |
750 | np_cfg_size = pci_func_count * sizeof(*np_cfg); |
751 | if (size != np_cfg_size) | |
ec079a07 SC |
752 | return QL_STATUS_INVALID_PARAM; |
753 | ||
319ecf12 | 754 | memset(&nic_info, 0, sizeof(struct qlcnic_info)); |
2f514c52 JK |
755 | memset(buf, 0, np_cfg_size); |
756 | np_cfg = (struct qlcnic_npar_func_cfg *)buf; | |
319ecf12 | 757 | |
2f514c52 | 758 | for (i = 0; i < pci_func_count; i++) { |
319ecf12 | 759 | if (qlcnic_is_valid_nic_func(adapter, i) < 0) |
ec079a07 SC |
760 | continue; |
761 | ret = qlcnic_get_nic_info(adapter, &nic_info, i); | |
762 | if (ret) | |
763 | return ret; | |
35dafcb0 SC |
764 | if (!adapter->npars[i].eswitch_status) |
765 | continue; | |
ec079a07 SC |
766 | np_cfg[i].pci_func = i; |
767 | np_cfg[i].op_mode = (u8)nic_info.op_mode; | |
768 | np_cfg[i].port_num = nic_info.phys_port; | |
769 | np_cfg[i].fw_capab = nic_info.capabilities; | |
b66e29c9 | 770 | np_cfg[i].min_bw = nic_info.min_tx_bw; |
ec079a07 SC |
771 | np_cfg[i].max_bw = nic_info.max_tx_bw; |
772 | np_cfg[i].max_tx_queues = nic_info.max_tx_ques; | |
773 | np_cfg[i].max_rx_queues = nic_info.max_rx_ques; | |
774 | } | |
ec079a07 SC |
775 | return size; |
776 | } | |
777 | ||
b66e29c9 SC |
778 | static ssize_t qlcnic_sysfs_get_port_stats(struct file *file, |
779 | struct kobject *kobj, | |
780 | struct bin_attribute *attr, | |
781 | char *buf, loff_t offset, | |
782 | size_t size) | |
ec079a07 SC |
783 | { |
784 | struct device *dev = container_of(kobj, struct device, kobj); | |
785 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
2f514c52 | 786 | u32 pci_func_count = qlcnic_get_pci_func_count(adapter); |
ec079a07 SC |
787 | struct qlcnic_esw_statistics port_stats; |
788 | int ret; | |
789 | ||
319ecf12 SC |
790 | if (qlcnic_83xx_check(adapter)) |
791 | return QLC_STATUS_UNSUPPORTED_CMD; | |
792 | ||
ec079a07 SC |
793 | if (size != sizeof(struct qlcnic_esw_statistics)) |
794 | return QL_STATUS_INVALID_PARAM; | |
795 | ||
2f514c52 | 796 | if (offset >= pci_func_count) |
ec079a07 SC |
797 | return QL_STATUS_INVALID_PARAM; |
798 | ||
799 | memset(&port_stats, 0, size); | |
800 | ret = qlcnic_get_port_stats(adapter, offset, QLCNIC_QUERY_RX_COUNTER, | |
b66e29c9 | 801 | &port_stats.rx); |
ec079a07 SC |
802 | if (ret) |
803 | return ret; | |
804 | ||
805 | ret = qlcnic_get_port_stats(adapter, offset, QLCNIC_QUERY_TX_COUNTER, | |
b66e29c9 | 806 | &port_stats.tx); |
ec079a07 SC |
807 | if (ret) |
808 | return ret; | |
809 | ||
810 | memcpy(buf, &port_stats, size); | |
811 | return size; | |
812 | } | |
813 | ||
b66e29c9 SC |
814 | static ssize_t qlcnic_sysfs_get_esw_stats(struct file *file, |
815 | struct kobject *kobj, | |
816 | struct bin_attribute *attr, | |
817 | char *buf, loff_t offset, | |
818 | size_t size) | |
ec079a07 SC |
819 | { |
820 | struct device *dev = container_of(kobj, struct device, kobj); | |
821 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
822 | struct qlcnic_esw_statistics esw_stats; | |
823 | int ret; | |
824 | ||
319ecf12 SC |
825 | if (qlcnic_83xx_check(adapter)) |
826 | return QLC_STATUS_UNSUPPORTED_CMD; | |
827 | ||
ec079a07 SC |
828 | if (size != sizeof(struct qlcnic_esw_statistics)) |
829 | return QL_STATUS_INVALID_PARAM; | |
830 | ||
831 | if (offset >= QLCNIC_NIU_MAX_XG_PORTS) | |
832 | return QL_STATUS_INVALID_PARAM; | |
833 | ||
834 | memset(&esw_stats, 0, size); | |
835 | ret = qlcnic_get_eswitch_stats(adapter, offset, QLCNIC_QUERY_RX_COUNTER, | |
b66e29c9 | 836 | &esw_stats.rx); |
ec079a07 SC |
837 | if (ret) |
838 | return ret; | |
839 | ||
840 | ret = qlcnic_get_eswitch_stats(adapter, offset, QLCNIC_QUERY_TX_COUNTER, | |
b66e29c9 | 841 | &esw_stats.tx); |
ec079a07 SC |
842 | if (ret) |
843 | return ret; | |
844 | ||
845 | memcpy(buf, &esw_stats, size); | |
846 | return size; | |
847 | } | |
848 | ||
b66e29c9 SC |
849 | static ssize_t qlcnic_sysfs_clear_esw_stats(struct file *file, |
850 | struct kobject *kobj, | |
851 | struct bin_attribute *attr, | |
852 | char *buf, loff_t offset, | |
853 | size_t size) | |
ec079a07 SC |
854 | { |
855 | struct device *dev = container_of(kobj, struct device, kobj); | |
856 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
857 | int ret; | |
858 | ||
319ecf12 SC |
859 | if (qlcnic_83xx_check(adapter)) |
860 | return QLC_STATUS_UNSUPPORTED_CMD; | |
861 | ||
ec079a07 SC |
862 | if (offset >= QLCNIC_NIU_MAX_XG_PORTS) |
863 | return QL_STATUS_INVALID_PARAM; | |
864 | ||
865 | ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_ESWITCH, offset, | |
b66e29c9 | 866 | QLCNIC_QUERY_RX_COUNTER); |
ec079a07 SC |
867 | if (ret) |
868 | return ret; | |
869 | ||
870 | ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_ESWITCH, offset, | |
b66e29c9 | 871 | QLCNIC_QUERY_TX_COUNTER); |
ec079a07 SC |
872 | if (ret) |
873 | return ret; | |
874 | ||
875 | return size; | |
876 | } | |
877 | ||
b66e29c9 SC |
878 | static ssize_t qlcnic_sysfs_clear_port_stats(struct file *file, |
879 | struct kobject *kobj, | |
880 | struct bin_attribute *attr, | |
881 | char *buf, loff_t offset, | |
882 | size_t size) | |
ec079a07 | 883 | { |
319ecf12 | 884 | |
ec079a07 SC |
885 | struct device *dev = container_of(kobj, struct device, kobj); |
886 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
2f514c52 | 887 | u32 pci_func_count = qlcnic_get_pci_func_count(adapter); |
ec079a07 SC |
888 | int ret; |
889 | ||
319ecf12 SC |
890 | if (qlcnic_83xx_check(adapter)) |
891 | return QLC_STATUS_UNSUPPORTED_CMD; | |
892 | ||
2f514c52 | 893 | if (offset >= pci_func_count) |
ec079a07 SC |
894 | return QL_STATUS_INVALID_PARAM; |
895 | ||
896 | ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_PORT, offset, | |
b66e29c9 | 897 | QLCNIC_QUERY_RX_COUNTER); |
ec079a07 SC |
898 | if (ret) |
899 | return ret; | |
900 | ||
901 | ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_PORT, offset, | |
b66e29c9 | 902 | QLCNIC_QUERY_TX_COUNTER); |
ec079a07 SC |
903 | if (ret) |
904 | return ret; | |
905 | ||
906 | return size; | |
907 | } | |
908 | ||
b66e29c9 SC |
909 | static ssize_t qlcnic_sysfs_read_pci_config(struct file *file, |
910 | struct kobject *kobj, | |
911 | struct bin_attribute *attr, | |
912 | char *buf, loff_t offset, | |
913 | size_t size) | |
ec079a07 SC |
914 | { |
915 | struct device *dev = container_of(kobj, struct device, kobj); | |
916 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
2f514c52 JK |
917 | u32 pci_func_count = qlcnic_get_pci_func_count(adapter); |
918 | struct qlcnic_pci_func_cfg *pci_cfg; | |
ec079a07 | 919 | struct qlcnic_pci_info *pci_info; |
f3c0773f | 920 | size_t pci_cfg_sz; |
ec079a07 SC |
921 | int i, ret; |
922 | ||
2f514c52 JK |
923 | pci_cfg_sz = pci_func_count * sizeof(*pci_cfg); |
924 | if (size != pci_cfg_sz) | |
ec079a07 SC |
925 | return QL_STATUS_INVALID_PARAM; |
926 | ||
f3c0773f | 927 | pci_info = kcalloc(pci_func_count, sizeof(*pci_info), GFP_KERNEL); |
ec079a07 SC |
928 | if (!pci_info) |
929 | return -ENOMEM; | |
930 | ||
931 | ret = qlcnic_get_pci_info(adapter, pci_info); | |
932 | if (ret) { | |
f3c0773f | 933 | kfree(pci_info); |
ec079a07 SC |
934 | return ret; |
935 | } | |
936 | ||
f3c0773f | 937 | pci_cfg = (struct qlcnic_pci_func_cfg *)buf; |
2f514c52 | 938 | for (i = 0; i < pci_func_count; i++) { |
ec079a07 SC |
939 | pci_cfg[i].pci_func = pci_info[i].id; |
940 | pci_cfg[i].func_type = pci_info[i].type; | |
f3c0773f | 941 | pci_cfg[i].func_state = 0; |
ec079a07 SC |
942 | pci_cfg[i].port_num = pci_info[i].default_port; |
943 | pci_cfg[i].min_bw = pci_info[i].tx_min_bw; | |
944 | pci_cfg[i].max_bw = pci_info[i].tx_max_bw; | |
945 | memcpy(&pci_cfg[i].def_mac_addr, &pci_info[i].mac, ETH_ALEN); | |
946 | } | |
319ecf12 | 947 | |
f3c0773f | 948 | kfree(pci_info); |
ec079a07 SC |
949 | return size; |
950 | } | |
951 | ||
a520030e HM |
952 | static ssize_t qlcnic_83xx_sysfs_flash_read_handler(struct file *filp, |
953 | struct kobject *kobj, | |
954 | struct bin_attribute *attr, | |
955 | char *buf, loff_t offset, | |
956 | size_t size) | |
957 | { | |
958 | unsigned char *p_read_buf; | |
959 | int ret, count; | |
960 | struct device *dev = container_of(kobj, struct device, kobj); | |
961 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
962 | ||
963 | if (!size) | |
964 | return QL_STATUS_INVALID_PARAM; | |
965 | if (!buf) | |
966 | return QL_STATUS_INVALID_PARAM; | |
967 | ||
968 | count = size / sizeof(u32); | |
969 | ||
970 | if (size % sizeof(u32)) | |
971 | count++; | |
972 | ||
973 | p_read_buf = kcalloc(size, sizeof(unsigned char), GFP_KERNEL); | |
974 | if (!p_read_buf) | |
975 | return -ENOMEM; | |
976 | if (qlcnic_83xx_lock_flash(adapter) != 0) { | |
977 | kfree(p_read_buf); | |
978 | return -EIO; | |
979 | } | |
980 | ||
981 | ret = qlcnic_83xx_lockless_flash_read32(adapter, offset, p_read_buf, | |
982 | count); | |
983 | ||
984 | if (ret) { | |
985 | qlcnic_83xx_unlock_flash(adapter); | |
986 | kfree(p_read_buf); | |
987 | return ret; | |
988 | } | |
989 | ||
990 | qlcnic_83xx_unlock_flash(adapter); | |
991 | memcpy(buf, p_read_buf, size); | |
992 | kfree(p_read_buf); | |
993 | ||
994 | return size; | |
995 | } | |
996 | ||
997 | static int qlcnic_83xx_sysfs_flash_bulk_write(struct qlcnic_adapter *adapter, | |
998 | char *buf, loff_t offset, | |
999 | size_t size) | |
1000 | { | |
1001 | int i, ret, count; | |
1002 | unsigned char *p_cache, *p_src; | |
1003 | ||
1004 | p_cache = kcalloc(size, sizeof(unsigned char), GFP_KERNEL); | |
1005 | if (!p_cache) | |
1006 | return -ENOMEM; | |
1007 | ||
1008 | memcpy(p_cache, buf, size); | |
1009 | p_src = p_cache; | |
1010 | count = size / sizeof(u32); | |
1011 | ||
1012 | if (qlcnic_83xx_lock_flash(adapter) != 0) { | |
1013 | kfree(p_cache); | |
1014 | return -EIO; | |
1015 | } | |
1016 | ||
1017 | if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { | |
1018 | ret = qlcnic_83xx_enable_flash_write(adapter); | |
1019 | if (ret) { | |
1020 | kfree(p_cache); | |
1021 | qlcnic_83xx_unlock_flash(adapter); | |
1022 | return -EIO; | |
1023 | } | |
1024 | } | |
1025 | ||
1026 | for (i = 0; i < count / QLC_83XX_FLASH_WRITE_MAX; i++) { | |
1027 | ret = qlcnic_83xx_flash_bulk_write(adapter, offset, | |
1028 | (u32 *)p_src, | |
1029 | QLC_83XX_FLASH_WRITE_MAX); | |
1030 | ||
1031 | if (ret) { | |
1032 | if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { | |
1033 | ret = qlcnic_83xx_disable_flash_write(adapter); | |
1034 | if (ret) { | |
1035 | kfree(p_cache); | |
1036 | qlcnic_83xx_unlock_flash(adapter); | |
1037 | return -EIO; | |
1038 | } | |
1039 | } | |
1040 | ||
1041 | kfree(p_cache); | |
1042 | qlcnic_83xx_unlock_flash(adapter); | |
1043 | return -EIO; | |
1044 | } | |
1045 | ||
1046 | p_src = p_src + sizeof(u32)*QLC_83XX_FLASH_WRITE_MAX; | |
1047 | offset = offset + sizeof(u32)*QLC_83XX_FLASH_WRITE_MAX; | |
1048 | } | |
1049 | ||
1050 | if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { | |
1051 | ret = qlcnic_83xx_disable_flash_write(adapter); | |
1052 | if (ret) { | |
1053 | kfree(p_cache); | |
1054 | qlcnic_83xx_unlock_flash(adapter); | |
1055 | return -EIO; | |
1056 | } | |
1057 | } | |
1058 | ||
1059 | kfree(p_cache); | |
1060 | qlcnic_83xx_unlock_flash(adapter); | |
1061 | ||
1062 | return 0; | |
1063 | } | |
1064 | ||
1065 | static int qlcnic_83xx_sysfs_flash_write(struct qlcnic_adapter *adapter, | |
1066 | char *buf, loff_t offset, size_t size) | |
1067 | { | |
1068 | int i, ret, count; | |
1069 | unsigned char *p_cache, *p_src; | |
1070 | ||
1071 | p_cache = kcalloc(size, sizeof(unsigned char), GFP_KERNEL); | |
1072 | if (!p_cache) | |
1073 | return -ENOMEM; | |
1074 | ||
1075 | memcpy(p_cache, buf, size); | |
1076 | p_src = p_cache; | |
1077 | count = size / sizeof(u32); | |
1078 | ||
1079 | if (qlcnic_83xx_lock_flash(adapter) != 0) { | |
1080 | kfree(p_cache); | |
1081 | return -EIO; | |
1082 | } | |
1083 | ||
1084 | if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { | |
1085 | ret = qlcnic_83xx_enable_flash_write(adapter); | |
1086 | if (ret) { | |
1087 | kfree(p_cache); | |
1088 | qlcnic_83xx_unlock_flash(adapter); | |
1089 | return -EIO; | |
1090 | } | |
1091 | } | |
1092 | ||
1093 | for (i = 0; i < count; i++) { | |
1094 | ret = qlcnic_83xx_flash_write32(adapter, offset, (u32 *)p_src); | |
1095 | if (ret) { | |
1096 | if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { | |
1097 | ret = qlcnic_83xx_disable_flash_write(adapter); | |
1098 | if (ret) { | |
1099 | kfree(p_cache); | |
1100 | qlcnic_83xx_unlock_flash(adapter); | |
1101 | return -EIO; | |
1102 | } | |
1103 | } | |
1104 | kfree(p_cache); | |
1105 | qlcnic_83xx_unlock_flash(adapter); | |
1106 | return -EIO; | |
1107 | } | |
1108 | ||
1109 | p_src = p_src + sizeof(u32); | |
1110 | offset = offset + sizeof(u32); | |
1111 | } | |
1112 | ||
1113 | if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { | |
1114 | ret = qlcnic_83xx_disable_flash_write(adapter); | |
1115 | if (ret) { | |
1116 | kfree(p_cache); | |
1117 | qlcnic_83xx_unlock_flash(adapter); | |
1118 | return -EIO; | |
1119 | } | |
1120 | } | |
1121 | ||
1122 | kfree(p_cache); | |
1123 | qlcnic_83xx_unlock_flash(adapter); | |
1124 | ||
1125 | return 0; | |
1126 | } | |
1127 | ||
1128 | static ssize_t qlcnic_83xx_sysfs_flash_write_handler(struct file *filp, | |
1129 | struct kobject *kobj, | |
1130 | struct bin_attribute *attr, | |
1131 | char *buf, loff_t offset, | |
1132 | size_t size) | |
1133 | { | |
1134 | int ret; | |
1135 | static int flash_mode; | |
1136 | unsigned long data; | |
1137 | struct device *dev = container_of(kobj, struct device, kobj); | |
1138 | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | |
1139 | ||
1140 | if (!buf) | |
1141 | return QL_STATUS_INVALID_PARAM; | |
1142 | ||
1143 | ret = kstrtoul(buf, 16, &data); | |
1144 | ||
1145 | switch (data) { | |
1146 | case QLC_83XX_FLASH_SECTOR_ERASE_CMD: | |
1147 | flash_mode = QLC_83XX_ERASE_MODE; | |
1148 | ret = qlcnic_83xx_erase_flash_sector(adapter, offset); | |
1149 | if (ret) { | |
1150 | dev_err(&adapter->pdev->dev, | |
1151 | "%s failed at %d\n", __func__, __LINE__); | |
1152 | return -EIO; | |
1153 | } | |
1154 | break; | |
1155 | ||
1156 | case QLC_83XX_FLASH_BULK_WRITE_CMD: | |
1157 | flash_mode = QLC_83XX_BULK_WRITE_MODE; | |
1158 | break; | |
1159 | ||
1160 | case QLC_83XX_FLASH_WRITE_CMD: | |
1161 | flash_mode = QLC_83XX_WRITE_MODE; | |
1162 | break; | |
1163 | default: | |
1164 | if (flash_mode == QLC_83XX_BULK_WRITE_MODE) { | |
1165 | ret = qlcnic_83xx_sysfs_flash_bulk_write(adapter, buf, | |
1166 | offset, size); | |
1167 | if (ret) { | |
1168 | dev_err(&adapter->pdev->dev, | |
1169 | "%s failed at %d\n", | |
1170 | __func__, __LINE__); | |
1171 | return -EIO; | |
1172 | } | |
1173 | } | |
1174 | ||
1175 | if (flash_mode == QLC_83XX_WRITE_MODE) { | |
1176 | ret = qlcnic_83xx_sysfs_flash_write(adapter, buf, | |
1177 | offset, size); | |
1178 | if (ret) { | |
1179 | dev_err(&adapter->pdev->dev, | |
1180 | "%s failed at %d\n", __func__, | |
1181 | __LINE__); | |
1182 | return -EIO; | |
1183 | } | |
1184 | } | |
1185 | } | |
1186 | ||
1187 | return size; | |
1188 | } | |
1189 | ||
ec079a07 SC |
1190 | static struct device_attribute dev_attr_bridged_mode = { |
1191 | .attr = {.name = "bridged_mode", .mode = (S_IRUGO | S_IWUSR)}, | |
1192 | .show = qlcnic_show_bridged_mode, | |
1193 | .store = qlcnic_store_bridged_mode, | |
1194 | }; | |
1195 | ||
1196 | static struct device_attribute dev_attr_diag_mode = { | |
1197 | .attr = {.name = "diag_mode", .mode = (S_IRUGO | S_IWUSR)}, | |
1198 | .show = qlcnic_show_diag_mode, | |
1199 | .store = qlcnic_store_diag_mode, | |
1200 | }; | |
1201 | ||
1202 | static struct device_attribute dev_attr_beacon = { | |
1203 | .attr = {.name = "beacon", .mode = (S_IRUGO | S_IWUSR)}, | |
1204 | .show = qlcnic_show_beacon, | |
1205 | .store = qlcnic_store_beacon, | |
1206 | }; | |
1207 | ||
1208 | static struct bin_attribute bin_attr_crb = { | |
1209 | .attr = {.name = "crb", .mode = (S_IRUGO | S_IWUSR)}, | |
1210 | .size = 0, | |
1211 | .read = qlcnic_sysfs_read_crb, | |
1212 | .write = qlcnic_sysfs_write_crb, | |
1213 | }; | |
1214 | ||
1215 | static struct bin_attribute bin_attr_mem = { | |
1216 | .attr = {.name = "mem", .mode = (S_IRUGO | S_IWUSR)}, | |
1217 | .size = 0, | |
1218 | .read = qlcnic_sysfs_read_mem, | |
1219 | .write = qlcnic_sysfs_write_mem, | |
1220 | }; | |
1221 | ||
1222 | static struct bin_attribute bin_attr_npar_config = { | |
1223 | .attr = {.name = "npar_config", .mode = (S_IRUGO | S_IWUSR)}, | |
1224 | .size = 0, | |
1225 | .read = qlcnic_sysfs_read_npar_config, | |
1226 | .write = qlcnic_sysfs_write_npar_config, | |
1227 | }; | |
1228 | ||
1229 | static struct bin_attribute bin_attr_pci_config = { | |
1230 | .attr = {.name = "pci_config", .mode = (S_IRUGO | S_IWUSR)}, | |
1231 | .size = 0, | |
1232 | .read = qlcnic_sysfs_read_pci_config, | |
1233 | .write = NULL, | |
1234 | }; | |
1235 | ||
1236 | static struct bin_attribute bin_attr_port_stats = { | |
1237 | .attr = {.name = "port_stats", .mode = (S_IRUGO | S_IWUSR)}, | |
1238 | .size = 0, | |
1239 | .read = qlcnic_sysfs_get_port_stats, | |
1240 | .write = qlcnic_sysfs_clear_port_stats, | |
1241 | }; | |
1242 | ||
1243 | static struct bin_attribute bin_attr_esw_stats = { | |
1244 | .attr = {.name = "esw_stats", .mode = (S_IRUGO | S_IWUSR)}, | |
1245 | .size = 0, | |
1246 | .read = qlcnic_sysfs_get_esw_stats, | |
1247 | .write = qlcnic_sysfs_clear_esw_stats, | |
1248 | }; | |
1249 | ||
1250 | static struct bin_attribute bin_attr_esw_config = { | |
1251 | .attr = {.name = "esw_config", .mode = (S_IRUGO | S_IWUSR)}, | |
1252 | .size = 0, | |
1253 | .read = qlcnic_sysfs_read_esw_config, | |
1254 | .write = qlcnic_sysfs_write_esw_config, | |
1255 | }; | |
1256 | ||
1257 | static struct bin_attribute bin_attr_pm_config = { | |
1258 | .attr = {.name = "pm_config", .mode = (S_IRUGO | S_IWUSR)}, | |
1259 | .size = 0, | |
1260 | .read = qlcnic_sysfs_read_pm_config, | |
1261 | .write = qlcnic_sysfs_write_pm_config, | |
1262 | }; | |
1263 | ||
a520030e HM |
1264 | static struct bin_attribute bin_attr_flash = { |
1265 | .attr = {.name = "flash", .mode = (S_IRUGO | S_IWUSR)}, | |
1266 | .size = 0, | |
1267 | .read = qlcnic_83xx_sysfs_flash_read_handler, | |
1268 | .write = qlcnic_83xx_sysfs_flash_write_handler, | |
1269 | }; | |
1270 | ||
ec079a07 SC |
1271 | void qlcnic_create_sysfs_entries(struct qlcnic_adapter *adapter) |
1272 | { | |
1273 | struct device *dev = &adapter->pdev->dev; | |
1274 | ||
79788450 | 1275 | if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_BDG) |
ec079a07 SC |
1276 | if (device_create_file(dev, &dev_attr_bridged_mode)) |
1277 | dev_warn(dev, | |
b66e29c9 | 1278 | "failed to create bridged_mode sysfs entry\n"); |
ec079a07 SC |
1279 | } |
1280 | ||
1281 | void qlcnic_remove_sysfs_entries(struct qlcnic_adapter *adapter) | |
1282 | { | |
1283 | struct device *dev = &adapter->pdev->dev; | |
1284 | ||
79788450 | 1285 | if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_BDG) |
ec079a07 SC |
1286 | device_remove_file(dev, &dev_attr_bridged_mode); |
1287 | } | |
1288 | ||
21041400 | 1289 | static void qlcnic_create_diag_entries(struct qlcnic_adapter *adapter) |
ec079a07 SC |
1290 | { |
1291 | struct device *dev = &adapter->pdev->dev; | |
ec079a07 SC |
1292 | |
1293 | if (device_create_bin_file(dev, &bin_attr_port_stats)) | |
1294 | dev_info(dev, "failed to create port stats sysfs entry"); | |
1295 | ||
79788450 | 1296 | if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) |
ec079a07 SC |
1297 | return; |
1298 | if (device_create_file(dev, &dev_attr_diag_mode)) | |
1299 | dev_info(dev, "failed to create diag_mode sysfs entry\n"); | |
1300 | if (device_create_bin_file(dev, &bin_attr_crb)) | |
1301 | dev_info(dev, "failed to create crb sysfs entry\n"); | |
1302 | if (device_create_bin_file(dev, &bin_attr_mem)) | |
1303 | dev_info(dev, "failed to create mem sysfs entry\n"); | |
1304 | ||
78ea2d97 | 1305 | if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) |
66451615 SC |
1306 | return; |
1307 | ||
ec079a07 SC |
1308 | if (device_create_bin_file(dev, &bin_attr_pci_config)) |
1309 | dev_info(dev, "failed to create pci config sysfs entry"); | |
66451615 | 1310 | |
ec079a07 SC |
1311 | if (device_create_file(dev, &dev_attr_beacon)) |
1312 | dev_info(dev, "failed to create beacon sysfs entry"); | |
1313 | ||
1314 | if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) | |
1315 | return; | |
1316 | if (device_create_bin_file(dev, &bin_attr_esw_config)) | |
1317 | dev_info(dev, "failed to create esw config sysfs entry"); | |
79788450 | 1318 | if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC) |
ec079a07 SC |
1319 | return; |
1320 | if (device_create_bin_file(dev, &bin_attr_npar_config)) | |
1321 | dev_info(dev, "failed to create npar config sysfs entry"); | |
1322 | if (device_create_bin_file(dev, &bin_attr_pm_config)) | |
1323 | dev_info(dev, "failed to create pm config sysfs entry"); | |
1324 | if (device_create_bin_file(dev, &bin_attr_esw_stats)) | |
1325 | dev_info(dev, "failed to create eswitch stats sysfs entry"); | |
1326 | } | |
1327 | ||
21041400 | 1328 | static void qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter) |
ec079a07 SC |
1329 | { |
1330 | struct device *dev = &adapter->pdev->dev; | |
ec079a07 SC |
1331 | |
1332 | device_remove_bin_file(dev, &bin_attr_port_stats); | |
1333 | ||
79788450 | 1334 | if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) |
ec079a07 SC |
1335 | return; |
1336 | device_remove_file(dev, &dev_attr_diag_mode); | |
1337 | device_remove_bin_file(dev, &bin_attr_crb); | |
1338 | device_remove_bin_file(dev, &bin_attr_mem); | |
66451615 | 1339 | |
78ea2d97 | 1340 | if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) |
66451615 SC |
1341 | return; |
1342 | ||
ec079a07 SC |
1343 | device_remove_bin_file(dev, &bin_attr_pci_config); |
1344 | device_remove_file(dev, &dev_attr_beacon); | |
1345 | if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) | |
1346 | return; | |
1347 | device_remove_bin_file(dev, &bin_attr_esw_config); | |
79788450 | 1348 | if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC) |
ec079a07 SC |
1349 | return; |
1350 | device_remove_bin_file(dev, &bin_attr_npar_config); | |
1351 | device_remove_bin_file(dev, &bin_attr_pm_config); | |
1352 | device_remove_bin_file(dev, &bin_attr_esw_stats); | |
1353 | } | |
7e2cf4fe SC |
1354 | |
1355 | void qlcnic_82xx_add_sysfs(struct qlcnic_adapter *adapter) | |
1356 | { | |
1357 | qlcnic_create_diag_entries(adapter); | |
1358 | } | |
1359 | ||
1360 | void qlcnic_82xx_remove_sysfs(struct qlcnic_adapter *adapter) | |
1361 | { | |
1362 | qlcnic_remove_diag_entries(adapter); | |
1363 | } | |
319ecf12 SC |
1364 | |
1365 | void qlcnic_83xx_add_sysfs(struct qlcnic_adapter *adapter) | |
1366 | { | |
a520030e HM |
1367 | struct device *dev = &adapter->pdev->dev; |
1368 | ||
319ecf12 | 1369 | qlcnic_create_diag_entries(adapter); |
a520030e HM |
1370 | |
1371 | if (sysfs_create_bin_file(&dev->kobj, &bin_attr_flash)) | |
1372 | dev_info(dev, "failed to create flash sysfs entry\n"); | |
319ecf12 SC |
1373 | } |
1374 | ||
1375 | void qlcnic_83xx_remove_sysfs(struct qlcnic_adapter *adapter) | |
1376 | { | |
a520030e HM |
1377 | struct device *dev = &adapter->pdev->dev; |
1378 | ||
319ecf12 | 1379 | qlcnic_remove_diag_entries(adapter); |
a520030e | 1380 | sysfs_remove_bin_file(&dev->kobj, &bin_attr_flash); |
319ecf12 | 1381 | } |