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