|  | // SPDX-License-Identifier: GPL-2.0-only | 
|  | /* | 
|  | * QLogic qlcnic NIC Driver | 
|  | * Copyright (c) 2009-2013 QLogic Corporation | 
|  | */ | 
|  |  | 
|  | #include <linux/slab.h> | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/swab.h> | 
|  | #include <linux/dma-mapping.h> | 
|  | #include <net/ip.h> | 
|  | #include <linux/ipv6.h> | 
|  | #include <linux/inetdevice.h> | 
|  | #include <linux/sysfs.h> | 
|  | #include <linux/log2.h> | 
|  | #ifdef CONFIG_QLCNIC_HWMON | 
|  | #include <linux/hwmon.h> | 
|  | #include <linux/hwmon-sysfs.h> | 
|  | #endif | 
|  |  | 
|  | #include "qlcnic.h" | 
|  | #include "qlcnic_hw.h" | 
|  |  | 
|  | int qlcnicvf_config_bridged_mode(struct qlcnic_adapter *adapter, u32 enable) | 
|  | { | 
|  | return -EOPNOTSUPP; | 
|  | } | 
|  |  | 
|  | int qlcnicvf_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate) | 
|  | { | 
|  | return -EOPNOTSUPP; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_store_bridged_mode(struct device *dev, | 
|  | struct device_attribute *attr, | 
|  | const char *buf, size_t len) | 
|  | { | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | unsigned long new; | 
|  | int ret = -EINVAL; | 
|  |  | 
|  | if (!(adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_BDG)) | 
|  | goto err_out; | 
|  |  | 
|  | if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) | 
|  | goto err_out; | 
|  |  | 
|  | if (kstrtoul(buf, 2, &new)) | 
|  | goto err_out; | 
|  |  | 
|  | if (!qlcnic_config_bridged_mode(adapter, !!new)) | 
|  | ret = len; | 
|  |  | 
|  | err_out: | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_show_bridged_mode(struct device *dev, | 
|  | struct device_attribute *attr, | 
|  | char *buf) | 
|  | { | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | int bridged_mode = 0; | 
|  |  | 
|  | if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_BDG) | 
|  | bridged_mode = !!(adapter->flags & QLCNIC_BRIDGE_ENABLED); | 
|  |  | 
|  | return sprintf(buf, "%d\n", bridged_mode); | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_store_diag_mode(struct device *dev, | 
|  | struct device_attribute *attr, | 
|  | const char *buf, size_t len) | 
|  | { | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | unsigned long new; | 
|  |  | 
|  | if (kstrtoul(buf, 2, &new)) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (!!new != !!(adapter->flags & QLCNIC_DIAG_ENABLED)) | 
|  | adapter->flags ^= QLCNIC_DIAG_ENABLED; | 
|  |  | 
|  | return len; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_show_diag_mode(struct device *dev, | 
|  | struct device_attribute *attr, char *buf) | 
|  | { | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | return sprintf(buf, "%d\n", !!(adapter->flags & QLCNIC_DIAG_ENABLED)); | 
|  | } | 
|  |  | 
|  | static int qlcnic_validate_beacon(struct qlcnic_adapter *adapter, u16 beacon, | 
|  | u8 *state, u8 *rate) | 
|  | { | 
|  | *rate = LSB(beacon); | 
|  | *state = MSB(beacon); | 
|  |  | 
|  | QLCDB(adapter, DRV, "rate %x state %x\n", *rate, *state); | 
|  |  | 
|  | if (!*state) { | 
|  | *rate = __QLCNIC_MAX_LED_RATE; | 
|  | return 0; | 
|  | } else if (*state > __QLCNIC_MAX_LED_STATE) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if ((!*rate) || (*rate > __QLCNIC_MAX_LED_RATE)) | 
|  | return -EINVAL; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int qlcnic_83xx_store_beacon(struct qlcnic_adapter *adapter, | 
|  | const char *buf, size_t len) | 
|  | { | 
|  | struct qlcnic_hardware_context *ahw = adapter->ahw; | 
|  | unsigned long h_beacon; | 
|  | int err; | 
|  |  | 
|  | if (test_bit(__QLCNIC_RESETTING, &adapter->state)) | 
|  | return -EIO; | 
|  |  | 
|  | if (kstrtoul(buf, 2, &h_beacon)) | 
|  | return -EINVAL; | 
|  |  | 
|  | qlcnic_get_beacon_state(adapter); | 
|  |  | 
|  | if (ahw->beacon_state == h_beacon) | 
|  | return len; | 
|  |  | 
|  | rtnl_lock(); | 
|  | if (!ahw->beacon_state) { | 
|  | if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) { | 
|  | rtnl_unlock(); | 
|  | return -EBUSY; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (h_beacon) | 
|  | err = qlcnic_83xx_config_led(adapter, 1, h_beacon); | 
|  | else | 
|  | err = qlcnic_83xx_config_led(adapter, 0, !h_beacon); | 
|  | if (!err) | 
|  | ahw->beacon_state = h_beacon; | 
|  |  | 
|  | if (!ahw->beacon_state) | 
|  | clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); | 
|  |  | 
|  | rtnl_unlock(); | 
|  | return len; | 
|  | } | 
|  |  | 
|  | static int qlcnic_82xx_store_beacon(struct qlcnic_adapter *adapter, | 
|  | const char *buf, size_t len) | 
|  | { | 
|  | struct qlcnic_hardware_context *ahw = adapter->ahw; | 
|  | int err, drv_sds_rings = adapter->drv_sds_rings; | 
|  | u16 beacon; | 
|  | u8 b_state, b_rate; | 
|  |  | 
|  | if (len != sizeof(u16)) | 
|  | return -EINVAL; | 
|  |  | 
|  | memcpy(&beacon, buf, sizeof(u16)); | 
|  | err = qlcnic_validate_beacon(adapter, beacon, &b_state, &b_rate); | 
|  | if (err) | 
|  | return err; | 
|  |  | 
|  | qlcnic_get_beacon_state(adapter); | 
|  |  | 
|  | if (ahw->beacon_state == b_state) | 
|  | return len; | 
|  |  | 
|  | rtnl_lock(); | 
|  | if (!ahw->beacon_state) { | 
|  | if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) { | 
|  | rtnl_unlock(); | 
|  | return -EBUSY; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (test_bit(__QLCNIC_RESETTING, &adapter->state)) { | 
|  | err = -EIO; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) { | 
|  | err = qlcnic_diag_alloc_res(adapter->netdev, QLCNIC_LED_TEST); | 
|  | if (err) | 
|  | goto out; | 
|  | set_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state); | 
|  | } | 
|  |  | 
|  | err = qlcnic_config_led(adapter, b_state, b_rate); | 
|  | if (!err) { | 
|  | err = len; | 
|  | ahw->beacon_state = b_state; | 
|  | } | 
|  |  | 
|  | if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state)) | 
|  | qlcnic_diag_free_res(adapter->netdev, drv_sds_rings); | 
|  |  | 
|  | out: | 
|  | if (!ahw->beacon_state) | 
|  | clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); | 
|  | rtnl_unlock(); | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_store_beacon(struct device *dev, | 
|  | struct device_attribute *attr, | 
|  | const char *buf, size_t len) | 
|  | { | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | int err = 0; | 
|  |  | 
|  | if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) { | 
|  | dev_warn(dev, | 
|  | "LED test not supported in non privileged mode\n"); | 
|  | return -EOPNOTSUPP; | 
|  | } | 
|  |  | 
|  | if (qlcnic_82xx_check(adapter)) | 
|  | err = qlcnic_82xx_store_beacon(adapter, buf, len); | 
|  | else if (qlcnic_83xx_check(adapter)) | 
|  | err = qlcnic_83xx_store_beacon(adapter, buf, len); | 
|  | else | 
|  | return -EIO; | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_show_beacon(struct device *dev, | 
|  | struct device_attribute *attr, char *buf) | 
|  | { | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  |  | 
|  | return sprintf(buf, "%d\n", adapter->ahw->beacon_state); | 
|  | } | 
|  |  | 
|  | static int qlcnic_sysfs_validate_crb(struct qlcnic_adapter *adapter, | 
|  | loff_t offset, size_t size) | 
|  | { | 
|  | size_t crb_size = 4; | 
|  |  | 
|  | if (!(adapter->flags & QLCNIC_DIAG_ENABLED)) | 
|  | return -EIO; | 
|  |  | 
|  | if (offset < QLCNIC_PCI_CRBSPACE) { | 
|  | if (ADDR_IN_RANGE(offset, QLCNIC_PCI_CAMQM, | 
|  | QLCNIC_PCI_CAMQM_END)) | 
|  | crb_size = 8; | 
|  | else | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if ((size != crb_size) || (offset & (crb_size-1))) | 
|  | return  -EINVAL; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_sysfs_read_crb(struct file *filp, struct kobject *kobj, | 
|  | const struct bin_attribute *attr, char *buf, | 
|  | loff_t offset, size_t size) | 
|  | { | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | int ret; | 
|  |  | 
|  | ret = qlcnic_sysfs_validate_crb(adapter, offset, size); | 
|  | if (ret != 0) | 
|  | return ret; | 
|  | qlcnic_read_crb(adapter, buf, offset, size); | 
|  | qlcnic_swap32_buffer((u32 *)buf, size / sizeof(u32)); | 
|  |  | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_sysfs_write_crb(struct file *filp, struct kobject *kobj, | 
|  | const struct bin_attribute *attr, char *buf, | 
|  | loff_t offset, size_t size) | 
|  | { | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | int ret; | 
|  |  | 
|  | ret = qlcnic_sysfs_validate_crb(adapter, offset, size); | 
|  | if (ret != 0) | 
|  | return ret; | 
|  |  | 
|  | qlcnic_swap32_buffer((u32 *)buf, size / sizeof(u32)); | 
|  | qlcnic_write_crb(adapter, buf, offset, size); | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static int qlcnic_sysfs_validate_mem(struct qlcnic_adapter *adapter, | 
|  | loff_t offset, size_t size) | 
|  | { | 
|  | if (!(adapter->flags & QLCNIC_DIAG_ENABLED)) | 
|  | return -EIO; | 
|  |  | 
|  | if ((size != 8) || (offset & 0x7)) | 
|  | return  -EIO; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_sysfs_read_mem(struct file *filp, struct kobject *kobj, | 
|  | const struct bin_attribute *attr, char *buf, | 
|  | loff_t offset, size_t size) | 
|  | { | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | u64 data; | 
|  | int ret; | 
|  |  | 
|  | ret = qlcnic_sysfs_validate_mem(adapter, offset, size); | 
|  | if (ret != 0) | 
|  | return ret; | 
|  |  | 
|  | if (qlcnic_pci_mem_read_2M(adapter, offset, &data)) | 
|  | return -EIO; | 
|  |  | 
|  | memcpy(buf, &data, size); | 
|  | qlcnic_swap32_buffer((u32 *)buf, size / sizeof(u32)); | 
|  |  | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_sysfs_write_mem(struct file *filp, struct kobject *kobj, | 
|  | const struct bin_attribute *attr, char *buf, | 
|  | loff_t offset, size_t size) | 
|  | { | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | u64 data; | 
|  | int ret; | 
|  |  | 
|  | ret = qlcnic_sysfs_validate_mem(adapter, offset, size); | 
|  | if (ret != 0) | 
|  | return ret; | 
|  |  | 
|  | qlcnic_swap32_buffer((u32 *)buf, size / sizeof(u32)); | 
|  | memcpy(&data, buf, size); | 
|  |  | 
|  | if (qlcnic_pci_mem_write_2M(adapter, offset, data)) | 
|  | return -EIO; | 
|  |  | 
|  | return size; | 
|  | } | 
|  |  | 
|  | int qlcnic_is_valid_nic_func(struct qlcnic_adapter *adapter, u8 pci_func) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < adapter->ahw->total_nic_func; i++) { | 
|  | if (adapter->npars[i].pci_func == pci_func) | 
|  | return i; | 
|  | } | 
|  |  | 
|  | dev_err(&adapter->pdev->dev, "%s: Invalid nic function\n", __func__); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | static int validate_pm_config(struct qlcnic_adapter *adapter, | 
|  | struct qlcnic_pm_func_cfg *pm_cfg, int count) | 
|  | { | 
|  | u8 src_pci_func, s_esw_id, d_esw_id; | 
|  | u8 dest_pci_func; | 
|  | int i, src_index, dest_index; | 
|  |  | 
|  | for (i = 0; i < count; i++) { | 
|  | src_pci_func = pm_cfg[i].pci_func; | 
|  | dest_pci_func = pm_cfg[i].dest_npar; | 
|  | src_index = qlcnic_is_valid_nic_func(adapter, src_pci_func); | 
|  | if (src_index < 0) | 
|  | return -EINVAL; | 
|  |  | 
|  | dest_index = qlcnic_is_valid_nic_func(adapter, dest_pci_func); | 
|  | if (dest_index < 0) | 
|  | return -EINVAL; | 
|  |  | 
|  | s_esw_id = adapter->npars[src_index].phy_port; | 
|  | d_esw_id = adapter->npars[dest_index].phy_port; | 
|  |  | 
|  | if (s_esw_id != d_esw_id) | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_sysfs_write_pm_config(struct file *filp, | 
|  | struct kobject *kobj, | 
|  | const struct bin_attribute *attr, | 
|  | char *buf, loff_t offset, | 
|  | size_t size) | 
|  | { | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | struct qlcnic_pm_func_cfg *pm_cfg; | 
|  | u32 id, action, pci_func; | 
|  | int count, rem, i, ret, index; | 
|  |  | 
|  | count	= size / sizeof(struct qlcnic_pm_func_cfg); | 
|  | rem	= size % sizeof(struct qlcnic_pm_func_cfg); | 
|  | if (rem) | 
|  | return -EINVAL; | 
|  |  | 
|  | qlcnic_swap32_buffer((u32 *)buf, size / sizeof(u32)); | 
|  | pm_cfg = (struct qlcnic_pm_func_cfg *)buf; | 
|  | ret = validate_pm_config(adapter, pm_cfg, count); | 
|  |  | 
|  | if (ret) | 
|  | return ret; | 
|  | for (i = 0; i < count; i++) { | 
|  | pci_func = pm_cfg[i].pci_func; | 
|  | action = !!pm_cfg[i].action; | 
|  | index = qlcnic_is_valid_nic_func(adapter, pci_func); | 
|  | if (index < 0) | 
|  | return -EINVAL; | 
|  |  | 
|  | id = adapter->npars[index].phy_port; | 
|  | ret = qlcnic_config_port_mirroring(adapter, id, | 
|  | action, pci_func); | 
|  | if (ret) | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < count; i++) { | 
|  | pci_func = pm_cfg[i].pci_func; | 
|  | index = qlcnic_is_valid_nic_func(adapter, pci_func); | 
|  | if (index < 0) | 
|  | return -EINVAL; | 
|  | id = adapter->npars[index].phy_port; | 
|  | adapter->npars[index].enable_pm = !!pm_cfg[i].action; | 
|  | adapter->npars[index].dest_npar = id; | 
|  | } | 
|  |  | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_sysfs_read_pm_config(struct file *filp, | 
|  | struct kobject *kobj, | 
|  | const struct bin_attribute *attr, | 
|  | char *buf, loff_t offset, | 
|  | size_t size) | 
|  | { | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | struct qlcnic_pm_func_cfg *pm_cfg; | 
|  | u8 pci_func; | 
|  | u32 count; | 
|  | int i; | 
|  |  | 
|  | memset(buf, 0, size); | 
|  | pm_cfg = (struct qlcnic_pm_func_cfg *)buf; | 
|  | count = size / sizeof(struct qlcnic_pm_func_cfg); | 
|  | for (i = 0; i < adapter->ahw->total_nic_func; i++) { | 
|  | pci_func = adapter->npars[i].pci_func; | 
|  | if (pci_func >= count) { | 
|  | dev_dbg(dev, "%s: Total nic functions[%d], App sent function count[%d]\n", | 
|  | __func__, adapter->ahw->total_nic_func, count); | 
|  | continue; | 
|  | } | 
|  | if (!adapter->npars[i].eswitch_status) | 
|  | continue; | 
|  |  | 
|  | pm_cfg[pci_func].action = adapter->npars[i].enable_pm; | 
|  | pm_cfg[pci_func].dest_npar = 0; | 
|  | pm_cfg[pci_func].pci_func = i; | 
|  | } | 
|  | qlcnic_swap32_buffer((u32 *)buf, size / sizeof(u32)); | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static int validate_esw_config(struct qlcnic_adapter *adapter, | 
|  | struct qlcnic_esw_func_cfg *esw_cfg, int count) | 
|  | { | 
|  | struct qlcnic_hardware_context *ahw = adapter->ahw; | 
|  | int i, ret; | 
|  | u32 op_mode; | 
|  | u8 pci_func; | 
|  |  | 
|  | if (qlcnic_82xx_check(adapter)) | 
|  | op_mode = readl(ahw->pci_base0 + QLCNIC_DRV_OP_MODE); | 
|  | else | 
|  | op_mode = QLCRDX(ahw, QLC_83XX_DRV_OP_MODE); | 
|  |  | 
|  | for (i = 0; i < count; i++) { | 
|  | pci_func = esw_cfg[i].pci_func; | 
|  | if (pci_func >= ahw->max_vnic_func) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (adapter->ahw->op_mode == QLCNIC_MGMT_FUNC) | 
|  | if (qlcnic_is_valid_nic_func(adapter, pci_func) < 0) | 
|  | return -EINVAL; | 
|  |  | 
|  | switch (esw_cfg[i].op_mode) { | 
|  | case QLCNIC_PORT_DEFAULTS: | 
|  | if (qlcnic_82xx_check(adapter)) { | 
|  | ret = QLC_DEV_GET_DRV(op_mode, pci_func); | 
|  | } else { | 
|  | ret = QLC_83XX_GET_FUNC_PRIVILEGE(op_mode, | 
|  | pci_func); | 
|  | esw_cfg[i].offload_flags = 0; | 
|  | } | 
|  |  | 
|  | if (ret != QLCNIC_NON_PRIV_FUNC) { | 
|  | if (esw_cfg[i].mac_anti_spoof != 0) | 
|  | return -EINVAL; | 
|  | if (esw_cfg[i].mac_override != 1) | 
|  | return -EINVAL; | 
|  | if (esw_cfg[i].promisc_mode != 1) | 
|  | return -EINVAL; | 
|  | } | 
|  | break; | 
|  | case QLCNIC_ADD_VLAN: | 
|  | if (!IS_VALID_VLAN(esw_cfg[i].vlan_id)) | 
|  | return -EINVAL; | 
|  | if (!esw_cfg[i].op_type) | 
|  | return -EINVAL; | 
|  | break; | 
|  | case QLCNIC_DEL_VLAN: | 
|  | if (!esw_cfg[i].op_type) | 
|  | return -EINVAL; | 
|  | break; | 
|  | default: | 
|  | return -EINVAL; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_sysfs_write_esw_config(struct file *file, | 
|  | struct kobject *kobj, | 
|  | const struct bin_attribute *attr, | 
|  | char *buf, loff_t offset, | 
|  | size_t size) | 
|  | { | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | struct qlcnic_esw_func_cfg *esw_cfg; | 
|  | struct qlcnic_npar_info *npar; | 
|  | int count, rem, i, ret; | 
|  | int index; | 
|  | u8 op_mode = 0, pci_func; | 
|  |  | 
|  | count	= size / sizeof(struct qlcnic_esw_func_cfg); | 
|  | rem	= size % sizeof(struct qlcnic_esw_func_cfg); | 
|  | if (rem) | 
|  | return -EINVAL; | 
|  |  | 
|  | qlcnic_swap32_buffer((u32 *)buf, size / sizeof(u32)); | 
|  | esw_cfg = (struct qlcnic_esw_func_cfg *)buf; | 
|  | ret = validate_esw_config(adapter, esw_cfg, count); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | for (i = 0; i < count; i++) { | 
|  | if (adapter->ahw->op_mode == QLCNIC_MGMT_FUNC) | 
|  | if (qlcnic_config_switch_port(adapter, &esw_cfg[i])) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (adapter->ahw->pci_func != esw_cfg[i].pci_func) | 
|  | continue; | 
|  |  | 
|  | op_mode = esw_cfg[i].op_mode; | 
|  | qlcnic_get_eswitch_port_config(adapter, &esw_cfg[i]); | 
|  | esw_cfg[i].op_mode = op_mode; | 
|  | esw_cfg[i].pci_func = adapter->ahw->pci_func; | 
|  |  | 
|  | switch (esw_cfg[i].op_mode) { | 
|  | case QLCNIC_PORT_DEFAULTS: | 
|  | qlcnic_set_eswitch_port_features(adapter, &esw_cfg[i]); | 
|  | rtnl_lock(); | 
|  | qlcnic_set_netdev_features(adapter, &esw_cfg[i]); | 
|  | rtnl_unlock(); | 
|  | break; | 
|  | case QLCNIC_ADD_VLAN: | 
|  | qlcnic_set_vlan_config(adapter, &esw_cfg[i]); | 
|  | break; | 
|  | case QLCNIC_DEL_VLAN: | 
|  | esw_cfg[i].vlan_id = 0; | 
|  | qlcnic_set_vlan_config(adapter, &esw_cfg[i]); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC) | 
|  | goto out; | 
|  |  | 
|  | for (i = 0; i < count; i++) { | 
|  | pci_func = esw_cfg[i].pci_func; | 
|  | index = qlcnic_is_valid_nic_func(adapter, pci_func); | 
|  | if (index < 0) | 
|  | return -EINVAL; | 
|  | npar = &adapter->npars[index]; | 
|  | switch (esw_cfg[i].op_mode) { | 
|  | case QLCNIC_PORT_DEFAULTS: | 
|  | npar->promisc_mode = esw_cfg[i].promisc_mode; | 
|  | npar->mac_override = esw_cfg[i].mac_override; | 
|  | npar->offload_flags = esw_cfg[i].offload_flags; | 
|  | npar->mac_anti_spoof = esw_cfg[i].mac_anti_spoof; | 
|  | npar->discard_tagged = esw_cfg[i].discard_tagged; | 
|  | break; | 
|  | case QLCNIC_ADD_VLAN: | 
|  | npar->pvid = esw_cfg[i].vlan_id; | 
|  | break; | 
|  | case QLCNIC_DEL_VLAN: | 
|  | npar->pvid = 0; | 
|  | break; | 
|  | } | 
|  | } | 
|  | out: | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_sysfs_read_esw_config(struct file *file, | 
|  | struct kobject *kobj, | 
|  | const struct bin_attribute *attr, | 
|  | char *buf, loff_t offset, | 
|  | size_t size) | 
|  | { | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | struct qlcnic_esw_func_cfg *esw_cfg; | 
|  | u8 pci_func; | 
|  | u32 count; | 
|  | int i; | 
|  |  | 
|  | memset(buf, 0, size); | 
|  | esw_cfg = (struct qlcnic_esw_func_cfg *)buf; | 
|  | count = size / sizeof(struct qlcnic_esw_func_cfg); | 
|  | for (i = 0; i < adapter->ahw->total_nic_func; i++) { | 
|  | pci_func = adapter->npars[i].pci_func; | 
|  | if (pci_func >= count) { | 
|  | dev_dbg(dev, "%s: Total nic functions[%d], App sent function count[%d]\n", | 
|  | __func__, adapter->ahw->total_nic_func, count); | 
|  | continue; | 
|  | } | 
|  | if (!adapter->npars[i].eswitch_status) | 
|  | continue; | 
|  |  | 
|  | esw_cfg[pci_func].pci_func = pci_func; | 
|  | if (qlcnic_get_eswitch_port_config(adapter, &esw_cfg[pci_func])) | 
|  | return -EINVAL; | 
|  | } | 
|  | qlcnic_swap32_buffer((u32 *)buf, size / sizeof(u32)); | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static int validate_npar_config(struct qlcnic_adapter *adapter, | 
|  | struct qlcnic_npar_func_cfg *np_cfg, | 
|  | int count) | 
|  | { | 
|  | u8 pci_func, i; | 
|  |  | 
|  | for (i = 0; i < count; i++) { | 
|  | pci_func = np_cfg[i].pci_func; | 
|  | if (qlcnic_is_valid_nic_func(adapter, pci_func) < 0) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (!IS_VALID_BW(np_cfg[i].min_bw) || | 
|  | !IS_VALID_BW(np_cfg[i].max_bw)) | 
|  | return -EINVAL; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_sysfs_write_npar_config(struct file *file, | 
|  | struct kobject *kobj, | 
|  | const struct bin_attribute *attr, | 
|  | char *buf, loff_t offset, | 
|  | size_t size) | 
|  | { | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | struct qlcnic_info nic_info; | 
|  | struct qlcnic_npar_func_cfg *np_cfg; | 
|  | int i, count, rem, ret, index; | 
|  | u8 pci_func; | 
|  |  | 
|  | count	= size / sizeof(struct qlcnic_npar_func_cfg); | 
|  | rem	= size % sizeof(struct qlcnic_npar_func_cfg); | 
|  | if (rem) | 
|  | return -EINVAL; | 
|  |  | 
|  | qlcnic_swap32_buffer((u32 *)buf, size / sizeof(u32)); | 
|  | np_cfg = (struct qlcnic_npar_func_cfg *)buf; | 
|  | ret = validate_npar_config(adapter, np_cfg, count); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | for (i = 0; i < count; i++) { | 
|  | pci_func = np_cfg[i].pci_func; | 
|  |  | 
|  | memset(&nic_info, 0, sizeof(struct qlcnic_info)); | 
|  | ret = qlcnic_get_nic_info(adapter, &nic_info, pci_func); | 
|  | if (ret) | 
|  | return ret; | 
|  | nic_info.pci_func = pci_func; | 
|  | nic_info.min_tx_bw = np_cfg[i].min_bw; | 
|  | nic_info.max_tx_bw = np_cfg[i].max_bw; | 
|  | ret = qlcnic_set_nic_info(adapter, &nic_info); | 
|  | if (ret) | 
|  | return ret; | 
|  | index = qlcnic_is_valid_nic_func(adapter, pci_func); | 
|  | if (index < 0) | 
|  | return -EINVAL; | 
|  | adapter->npars[index].min_bw = nic_info.min_tx_bw; | 
|  | adapter->npars[index].max_bw = nic_info.max_tx_bw; | 
|  | } | 
|  |  | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_sysfs_read_npar_config(struct file *file, | 
|  | struct kobject *kobj, | 
|  | const struct bin_attribute *attr, | 
|  | char *buf, loff_t offset, | 
|  | size_t size) | 
|  | { | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | struct qlcnic_npar_func_cfg *np_cfg; | 
|  | struct qlcnic_info nic_info; | 
|  | u8 pci_func; | 
|  | int i, ret; | 
|  | u32 count; | 
|  |  | 
|  | memset(&nic_info, 0, sizeof(struct qlcnic_info)); | 
|  | memset(buf, 0, size); | 
|  | np_cfg = (struct qlcnic_npar_func_cfg *)buf; | 
|  |  | 
|  | count = size / sizeof(struct qlcnic_npar_func_cfg); | 
|  | for (i = 0; i < adapter->ahw->total_nic_func; i++) { | 
|  | if (adapter->npars[i].pci_func >= count) { | 
|  | dev_dbg(dev, "%s: Total nic functions[%d], App sent function count[%d]\n", | 
|  | __func__, adapter->ahw->total_nic_func, count); | 
|  | continue; | 
|  | } | 
|  | if (!adapter->npars[i].eswitch_status) | 
|  | continue; | 
|  | pci_func = adapter->npars[i].pci_func; | 
|  | if (qlcnic_is_valid_nic_func(adapter, pci_func) < 0) | 
|  | continue; | 
|  | ret = qlcnic_get_nic_info(adapter, &nic_info, pci_func); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | np_cfg[pci_func].pci_func = pci_func; | 
|  | np_cfg[pci_func].op_mode = (u8)nic_info.op_mode; | 
|  | np_cfg[pci_func].port_num = nic_info.phys_port; | 
|  | np_cfg[pci_func].fw_capab = nic_info.capabilities; | 
|  | np_cfg[pci_func].min_bw = nic_info.min_tx_bw; | 
|  | np_cfg[pci_func].max_bw = nic_info.max_tx_bw; | 
|  | np_cfg[pci_func].max_tx_queues = nic_info.max_tx_ques; | 
|  | np_cfg[pci_func].max_rx_queues = nic_info.max_rx_ques; | 
|  | } | 
|  | qlcnic_swap32_buffer((u32 *)buf, size / sizeof(u32)); | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_sysfs_get_port_stats(struct file *file, | 
|  | struct kobject *kobj, | 
|  | const struct bin_attribute *attr, | 
|  | char *buf, loff_t offset, | 
|  | size_t size) | 
|  | { | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | struct qlcnic_esw_statistics port_stats; | 
|  | int ret; | 
|  |  | 
|  | if (qlcnic_83xx_check(adapter)) | 
|  | return -EOPNOTSUPP; | 
|  |  | 
|  | if (size != sizeof(struct qlcnic_esw_statistics)) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (offset >= adapter->ahw->max_vnic_func) | 
|  | return -EINVAL; | 
|  |  | 
|  | memset(&port_stats, 0, size); | 
|  | ret = qlcnic_get_port_stats(adapter, offset, QLCNIC_QUERY_RX_COUNTER, | 
|  | &port_stats.rx); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | ret = qlcnic_get_port_stats(adapter, offset, QLCNIC_QUERY_TX_COUNTER, | 
|  | &port_stats.tx); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | memcpy(buf, &port_stats, size); | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_sysfs_get_esw_stats(struct file *file, | 
|  | struct kobject *kobj, | 
|  | const struct bin_attribute *attr, | 
|  | char *buf, loff_t offset, | 
|  | size_t size) | 
|  | { | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | struct qlcnic_esw_statistics esw_stats; | 
|  | int ret; | 
|  |  | 
|  | if (qlcnic_83xx_check(adapter)) | 
|  | return -EOPNOTSUPP; | 
|  |  | 
|  | if (size != sizeof(struct qlcnic_esw_statistics)) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (offset >= QLCNIC_NIU_MAX_XG_PORTS) | 
|  | return -EINVAL; | 
|  |  | 
|  | memset(&esw_stats, 0, size); | 
|  | ret = qlcnic_get_eswitch_stats(adapter, offset, QLCNIC_QUERY_RX_COUNTER, | 
|  | &esw_stats.rx); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | ret = qlcnic_get_eswitch_stats(adapter, offset, QLCNIC_QUERY_TX_COUNTER, | 
|  | &esw_stats.tx); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | memcpy(buf, &esw_stats, size); | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_sysfs_clear_esw_stats(struct file *file, | 
|  | struct kobject *kobj, | 
|  | const struct bin_attribute *attr, | 
|  | char *buf, loff_t offset, | 
|  | size_t size) | 
|  | { | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | int ret; | 
|  |  | 
|  | if (qlcnic_83xx_check(adapter)) | 
|  | return -EOPNOTSUPP; | 
|  |  | 
|  | if (offset >= QLCNIC_NIU_MAX_XG_PORTS) | 
|  | return -EINVAL; | 
|  |  | 
|  | ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_ESWITCH, offset, | 
|  | QLCNIC_QUERY_RX_COUNTER); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_ESWITCH, offset, | 
|  | QLCNIC_QUERY_TX_COUNTER); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_sysfs_clear_port_stats(struct file *file, | 
|  | struct kobject *kobj, | 
|  | const struct bin_attribute *attr, | 
|  | char *buf, loff_t offset, | 
|  | size_t size) | 
|  | { | 
|  |  | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | int ret; | 
|  |  | 
|  | if (qlcnic_83xx_check(adapter)) | 
|  | return -EOPNOTSUPP; | 
|  |  | 
|  | if (offset >= adapter->ahw->max_vnic_func) | 
|  | return -EINVAL; | 
|  |  | 
|  | ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_PORT, offset, | 
|  | QLCNIC_QUERY_RX_COUNTER); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_PORT, offset, | 
|  | QLCNIC_QUERY_TX_COUNTER); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_sysfs_read_pci_config(struct file *file, | 
|  | struct kobject *kobj, | 
|  | const struct bin_attribute *attr, | 
|  | char *buf, loff_t offset, | 
|  | size_t size) | 
|  | { | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | struct qlcnic_pci_func_cfg *pci_cfg; | 
|  | struct qlcnic_pci_info *pci_info; | 
|  | int i, ret; | 
|  | u32 count; | 
|  |  | 
|  | pci_info = kcalloc(size, sizeof(*pci_info), GFP_KERNEL); | 
|  | if (!pci_info) | 
|  | return -ENOMEM; | 
|  |  | 
|  | ret = qlcnic_get_pci_info(adapter, pci_info); | 
|  | if (ret) { | 
|  | kfree(pci_info); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | pci_cfg = (struct qlcnic_pci_func_cfg *)buf; | 
|  | count = size / sizeof(struct qlcnic_pci_func_cfg); | 
|  | qlcnic_swap32_buffer((u32 *)pci_info, size / sizeof(u32)); | 
|  | for (i = 0; i < count; i++) { | 
|  | pci_cfg[i].pci_func = pci_info[i].id; | 
|  | pci_cfg[i].func_type = pci_info[i].type; | 
|  | pci_cfg[i].func_state = 0; | 
|  | pci_cfg[i].port_num = pci_info[i].default_port; | 
|  | pci_cfg[i].min_bw = pci_info[i].tx_min_bw; | 
|  | pci_cfg[i].max_bw = pci_info[i].tx_max_bw; | 
|  | memcpy(&pci_cfg[i].def_mac_addr, &pci_info[i].mac, ETH_ALEN); | 
|  | } | 
|  |  | 
|  | kfree(pci_info); | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_83xx_sysfs_flash_read_handler(struct file *filp, | 
|  | struct kobject *kobj, | 
|  | const struct bin_attribute *attr, | 
|  | char *buf, loff_t offset, | 
|  | size_t size) | 
|  | { | 
|  | unsigned char *p_read_buf; | 
|  | int  ret, count; | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  |  | 
|  | if (!size) | 
|  | return -EINVAL; | 
|  |  | 
|  | count = size / sizeof(u32); | 
|  |  | 
|  | if (size % sizeof(u32)) | 
|  | count++; | 
|  |  | 
|  | p_read_buf = kcalloc(size, sizeof(unsigned char), GFP_KERNEL); | 
|  | if (!p_read_buf) | 
|  | return -ENOMEM; | 
|  | if (qlcnic_83xx_lock_flash(adapter) != 0) { | 
|  | kfree(p_read_buf); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | ret = qlcnic_83xx_lockless_flash_read32(adapter, offset, p_read_buf, | 
|  | count); | 
|  |  | 
|  | if (ret) { | 
|  | qlcnic_83xx_unlock_flash(adapter); | 
|  | kfree(p_read_buf); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | qlcnic_83xx_unlock_flash(adapter); | 
|  | qlcnic_swap32_buffer((u32 *)p_read_buf, count); | 
|  | memcpy(buf, p_read_buf, size); | 
|  | kfree(p_read_buf); | 
|  |  | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static int qlcnic_83xx_sysfs_flash_bulk_write(struct qlcnic_adapter *adapter, | 
|  | char *buf, loff_t offset, | 
|  | size_t size) | 
|  | { | 
|  | int  i, ret, count; | 
|  | unsigned char *p_cache, *p_src; | 
|  |  | 
|  | p_cache = kcalloc(size, sizeof(unsigned char), GFP_KERNEL); | 
|  | if (!p_cache) | 
|  | return -ENOMEM; | 
|  |  | 
|  | count = size / sizeof(u32); | 
|  | qlcnic_swap32_buffer((u32 *)buf, count); | 
|  | memcpy(p_cache, buf, size); | 
|  | p_src = p_cache; | 
|  |  | 
|  | if (qlcnic_83xx_lock_flash(adapter) != 0) { | 
|  | kfree(p_cache); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { | 
|  | ret = qlcnic_83xx_enable_flash_write(adapter); | 
|  | if (ret) { | 
|  | kfree(p_cache); | 
|  | qlcnic_83xx_unlock_flash(adapter); | 
|  | return -EIO; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (i = 0; i < count / QLC_83XX_FLASH_WRITE_MAX; i++) { | 
|  | ret = qlcnic_83xx_flash_bulk_write(adapter, offset, | 
|  | (u32 *)p_src, | 
|  | QLC_83XX_FLASH_WRITE_MAX); | 
|  |  | 
|  | if (ret) { | 
|  | if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { | 
|  | ret = qlcnic_83xx_disable_flash_write(adapter); | 
|  | if (ret) { | 
|  | kfree(p_cache); | 
|  | qlcnic_83xx_unlock_flash(adapter); | 
|  | return -EIO; | 
|  | } | 
|  | } | 
|  |  | 
|  | kfree(p_cache); | 
|  | qlcnic_83xx_unlock_flash(adapter); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | p_src = p_src + sizeof(u32)*QLC_83XX_FLASH_WRITE_MAX; | 
|  | offset = offset + sizeof(u32)*QLC_83XX_FLASH_WRITE_MAX; | 
|  | } | 
|  |  | 
|  | if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { | 
|  | ret = qlcnic_83xx_disable_flash_write(adapter); | 
|  | if (ret) { | 
|  | kfree(p_cache); | 
|  | qlcnic_83xx_unlock_flash(adapter); | 
|  | return -EIO; | 
|  | } | 
|  | } | 
|  |  | 
|  | kfree(p_cache); | 
|  | qlcnic_83xx_unlock_flash(adapter); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int qlcnic_83xx_sysfs_flash_write(struct qlcnic_adapter *adapter, | 
|  | char *buf, loff_t offset, size_t size) | 
|  | { | 
|  | int  i, ret, count; | 
|  | unsigned char *p_cache, *p_src; | 
|  |  | 
|  | p_cache = kcalloc(size, sizeof(unsigned char), GFP_KERNEL); | 
|  | if (!p_cache) | 
|  | return -ENOMEM; | 
|  |  | 
|  | qlcnic_swap32_buffer((u32 *)buf, size / sizeof(u32)); | 
|  | memcpy(p_cache, buf, size); | 
|  | p_src = p_cache; | 
|  | count = size / sizeof(u32); | 
|  |  | 
|  | if (qlcnic_83xx_lock_flash(adapter) != 0) { | 
|  | kfree(p_cache); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { | 
|  | ret = qlcnic_83xx_enable_flash_write(adapter); | 
|  | if (ret) { | 
|  | kfree(p_cache); | 
|  | qlcnic_83xx_unlock_flash(adapter); | 
|  | return -EIO; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (i = 0; i < count; i++) { | 
|  | ret = qlcnic_83xx_flash_write32(adapter, offset, (u32 *)p_src); | 
|  | if (ret) { | 
|  | if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { | 
|  | ret = qlcnic_83xx_disable_flash_write(adapter); | 
|  | if (ret) { | 
|  | kfree(p_cache); | 
|  | qlcnic_83xx_unlock_flash(adapter); | 
|  | return -EIO; | 
|  | } | 
|  | } | 
|  | kfree(p_cache); | 
|  | qlcnic_83xx_unlock_flash(adapter); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | p_src = p_src + sizeof(u32); | 
|  | offset = offset + sizeof(u32); | 
|  | } | 
|  |  | 
|  | if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { | 
|  | ret = qlcnic_83xx_disable_flash_write(adapter); | 
|  | if (ret) { | 
|  | kfree(p_cache); | 
|  | qlcnic_83xx_unlock_flash(adapter); | 
|  | return -EIO; | 
|  | } | 
|  | } | 
|  |  | 
|  | kfree(p_cache); | 
|  | qlcnic_83xx_unlock_flash(adapter); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static ssize_t qlcnic_83xx_sysfs_flash_write_handler(struct file *filp, | 
|  | struct kobject *kobj, | 
|  | const struct bin_attribute *attr, | 
|  | char *buf, loff_t offset, | 
|  | size_t size) | 
|  | { | 
|  | int  ret; | 
|  | static int flash_mode; | 
|  | unsigned long data; | 
|  | struct device *dev = kobj_to_dev(kobj); | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  |  | 
|  | ret = kstrtoul(buf, 16, &data); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | switch (data) { | 
|  | case QLC_83XX_FLASH_SECTOR_ERASE_CMD: | 
|  | flash_mode = QLC_83XX_ERASE_MODE; | 
|  | ret = qlcnic_83xx_erase_flash_sector(adapter, offset); | 
|  | if (ret) { | 
|  | dev_err(&adapter->pdev->dev, | 
|  | "%s failed at %d\n", __func__, __LINE__); | 
|  | return -EIO; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case QLC_83XX_FLASH_BULK_WRITE_CMD: | 
|  | flash_mode = QLC_83XX_BULK_WRITE_MODE; | 
|  | break; | 
|  |  | 
|  | case QLC_83XX_FLASH_WRITE_CMD: | 
|  | flash_mode = QLC_83XX_WRITE_MODE; | 
|  | break; | 
|  | default: | 
|  | if (flash_mode == QLC_83XX_BULK_WRITE_MODE) { | 
|  | ret = qlcnic_83xx_sysfs_flash_bulk_write(adapter, buf, | 
|  | offset, size); | 
|  | if (ret) { | 
|  | dev_err(&adapter->pdev->dev, | 
|  | "%s failed at %d\n", | 
|  | __func__, __LINE__); | 
|  | return -EIO; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (flash_mode == QLC_83XX_WRITE_MODE) { | 
|  | ret = qlcnic_83xx_sysfs_flash_write(adapter, buf, | 
|  | offset, size); | 
|  | if (ret) { | 
|  | dev_err(&adapter->pdev->dev, | 
|  | "%s failed at %d\n", __func__, | 
|  | __LINE__); | 
|  | return -EIO; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static const struct device_attribute dev_attr_bridged_mode = { | 
|  | .attr = { .name = "bridged_mode", .mode = 0644 }, | 
|  | .show = qlcnic_show_bridged_mode, | 
|  | .store = qlcnic_store_bridged_mode, | 
|  | }; | 
|  |  | 
|  | static const struct device_attribute dev_attr_diag_mode = { | 
|  | .attr = { .name = "diag_mode", .mode = 0644 }, | 
|  | .show = qlcnic_show_diag_mode, | 
|  | .store = qlcnic_store_diag_mode, | 
|  | }; | 
|  |  | 
|  | static const struct device_attribute dev_attr_beacon = { | 
|  | .attr = { .name = "beacon", .mode = 0644 }, | 
|  | .show = qlcnic_show_beacon, | 
|  | .store = qlcnic_store_beacon, | 
|  | }; | 
|  |  | 
|  | static const struct bin_attribute bin_attr_crb = { | 
|  | .attr = { .name = "crb", .mode = 0644 }, | 
|  | .size = 0, | 
|  | .read_new = qlcnic_sysfs_read_crb, | 
|  | .write_new = qlcnic_sysfs_write_crb, | 
|  | }; | 
|  |  | 
|  | static const struct bin_attribute bin_attr_mem = { | 
|  | .attr = { .name = "mem", .mode = 0644 }, | 
|  | .size = 0, | 
|  | .read_new = qlcnic_sysfs_read_mem, | 
|  | .write_new = qlcnic_sysfs_write_mem, | 
|  | }; | 
|  |  | 
|  | static const struct bin_attribute bin_attr_npar_config = { | 
|  | .attr = { .name = "npar_config", .mode = 0644 }, | 
|  | .size = 0, | 
|  | .read_new = qlcnic_sysfs_read_npar_config, | 
|  | .write_new = qlcnic_sysfs_write_npar_config, | 
|  | }; | 
|  |  | 
|  | static const struct bin_attribute bin_attr_pci_config = { | 
|  | .attr = { .name = "pci_config", .mode = 0644 }, | 
|  | .size = 0, | 
|  | .read_new = qlcnic_sysfs_read_pci_config, | 
|  | }; | 
|  |  | 
|  | static const struct bin_attribute bin_attr_port_stats = { | 
|  | .attr = { .name = "port_stats", .mode = 0644 }, | 
|  | .size = 0, | 
|  | .read_new = qlcnic_sysfs_get_port_stats, | 
|  | .write_new = qlcnic_sysfs_clear_port_stats, | 
|  | }; | 
|  |  | 
|  | static const struct bin_attribute bin_attr_esw_stats = { | 
|  | .attr = { .name = "esw_stats", .mode = 0644 }, | 
|  | .size = 0, | 
|  | .read_new = qlcnic_sysfs_get_esw_stats, | 
|  | .write_new = qlcnic_sysfs_clear_esw_stats, | 
|  | }; | 
|  |  | 
|  | static const struct bin_attribute bin_attr_esw_config = { | 
|  | .attr = { .name = "esw_config", .mode = 0644 }, | 
|  | .size = 0, | 
|  | .read_new = qlcnic_sysfs_read_esw_config, | 
|  | .write_new = qlcnic_sysfs_write_esw_config, | 
|  | }; | 
|  |  | 
|  | static const struct bin_attribute bin_attr_pm_config = { | 
|  | .attr = { .name = "pm_config", .mode = 0644 }, | 
|  | .size = 0, | 
|  | .read_new = qlcnic_sysfs_read_pm_config, | 
|  | .write_new = qlcnic_sysfs_write_pm_config, | 
|  | }; | 
|  |  | 
|  | static const struct bin_attribute bin_attr_flash = { | 
|  | .attr = { .name = "flash", .mode = 0644 }, | 
|  | .size = 0, | 
|  | .read_new = qlcnic_83xx_sysfs_flash_read_handler, | 
|  | .write_new = qlcnic_83xx_sysfs_flash_write_handler, | 
|  | }; | 
|  |  | 
|  | #ifdef CONFIG_QLCNIC_HWMON | 
|  |  | 
|  | static ssize_t qlcnic_hwmon_show_temp(struct device *dev, | 
|  | struct device_attribute *dev_attr, | 
|  | char *buf) | 
|  | { | 
|  | struct qlcnic_adapter *adapter = dev_get_drvdata(dev); | 
|  | unsigned int temperature = 0, value = 0; | 
|  |  | 
|  | if (qlcnic_83xx_check(adapter)) | 
|  | value = QLCRDX(adapter->ahw, QLC_83XX_ASIC_TEMP); | 
|  | else if (qlcnic_82xx_check(adapter)) | 
|  | value = QLC_SHARED_REG_RD32(adapter, QLCNIC_ASIC_TEMP); | 
|  |  | 
|  | temperature = qlcnic_get_temp_val(value); | 
|  | /* display millidegree celcius */ | 
|  | temperature *= 1000; | 
|  | return sprintf(buf, "%u\n", temperature); | 
|  | } | 
|  |  | 
|  | /* hwmon-sysfs attributes */ | 
|  | static SENSOR_DEVICE_ATTR(temp1_input, 0444, | 
|  | qlcnic_hwmon_show_temp, NULL, 1); | 
|  |  | 
|  | static struct attribute *qlcnic_hwmon_attrs[] = { | 
|  | &sensor_dev_attr_temp1_input.dev_attr.attr, | 
|  | NULL | 
|  | }; | 
|  |  | 
|  | ATTRIBUTE_GROUPS(qlcnic_hwmon); | 
|  |  | 
|  | void qlcnic_register_hwmon_dev(struct qlcnic_adapter *adapter) | 
|  | { | 
|  | struct device *dev = &adapter->pdev->dev; | 
|  | struct device *hwmon_dev; | 
|  |  | 
|  | /* Skip hwmon registration for a VF device */ | 
|  | if (qlcnic_sriov_vf_check(adapter)) { | 
|  | adapter->ahw->hwmon_dev = NULL; | 
|  | return; | 
|  | } | 
|  | hwmon_dev = hwmon_device_register_with_groups(dev, qlcnic_driver_name, | 
|  | adapter, | 
|  | qlcnic_hwmon_groups); | 
|  | if (IS_ERR(hwmon_dev)) { | 
|  | dev_err(dev, "Cannot register with hwmon, err=%ld\n", | 
|  | PTR_ERR(hwmon_dev)); | 
|  | hwmon_dev = NULL; | 
|  | } | 
|  | adapter->ahw->hwmon_dev = hwmon_dev; | 
|  | } | 
|  |  | 
|  | void qlcnic_unregister_hwmon_dev(struct qlcnic_adapter *adapter) | 
|  | { | 
|  | struct device *hwmon_dev = adapter->ahw->hwmon_dev; | 
|  | if (hwmon_dev) { | 
|  | hwmon_device_unregister(hwmon_dev); | 
|  | adapter->ahw->hwmon_dev = NULL; | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | void qlcnic_create_sysfs_entries(struct qlcnic_adapter *adapter) | 
|  | { | 
|  | struct device *dev = &adapter->pdev->dev; | 
|  |  | 
|  | if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_BDG) | 
|  | if (device_create_file(dev, &dev_attr_bridged_mode)) | 
|  | dev_warn(dev, | 
|  | "failed to create bridged_mode sysfs entry\n"); | 
|  | } | 
|  |  | 
|  | void qlcnic_remove_sysfs_entries(struct qlcnic_adapter *adapter) | 
|  | { | 
|  | struct device *dev = &adapter->pdev->dev; | 
|  |  | 
|  | if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_BDG) | 
|  | device_remove_file(dev, &dev_attr_bridged_mode); | 
|  | } | 
|  |  | 
|  | static void qlcnic_create_diag_entries(struct qlcnic_adapter *adapter) | 
|  | { | 
|  | struct device *dev = &adapter->pdev->dev; | 
|  |  | 
|  | if (device_create_bin_file(dev, &bin_attr_port_stats)) | 
|  | dev_info(dev, "failed to create port stats sysfs entry"); | 
|  |  | 
|  | if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) | 
|  | return; | 
|  | if (device_create_file(dev, &dev_attr_diag_mode)) | 
|  | dev_info(dev, "failed to create diag_mode sysfs entry\n"); | 
|  | if (device_create_bin_file(dev, &bin_attr_crb)) | 
|  | dev_info(dev, "failed to create crb sysfs entry\n"); | 
|  | if (device_create_bin_file(dev, &bin_attr_mem)) | 
|  | dev_info(dev, "failed to create mem sysfs entry\n"); | 
|  |  | 
|  | if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) | 
|  | return; | 
|  |  | 
|  | if (device_create_bin_file(dev, &bin_attr_pci_config)) | 
|  | dev_info(dev, "failed to create pci config sysfs entry"); | 
|  |  | 
|  | if (device_create_file(dev, &dev_attr_beacon)) | 
|  | dev_info(dev, "failed to create beacon sysfs entry"); | 
|  |  | 
|  | if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) | 
|  | return; | 
|  | if (device_create_bin_file(dev, &bin_attr_esw_config)) | 
|  | dev_info(dev, "failed to create esw config sysfs entry"); | 
|  | if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC) | 
|  | return; | 
|  | if (device_create_bin_file(dev, &bin_attr_npar_config)) | 
|  | dev_info(dev, "failed to create npar config sysfs entry"); | 
|  | if (device_create_bin_file(dev, &bin_attr_pm_config)) | 
|  | dev_info(dev, "failed to create pm config sysfs entry"); | 
|  | if (device_create_bin_file(dev, &bin_attr_esw_stats)) | 
|  | dev_info(dev, "failed to create eswitch stats sysfs entry"); | 
|  | } | 
|  |  | 
|  | static void qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter) | 
|  | { | 
|  | struct device *dev = &adapter->pdev->dev; | 
|  |  | 
|  | device_remove_bin_file(dev, &bin_attr_port_stats); | 
|  |  | 
|  | if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) | 
|  | return; | 
|  | device_remove_file(dev, &dev_attr_diag_mode); | 
|  | device_remove_bin_file(dev, &bin_attr_crb); | 
|  | device_remove_bin_file(dev, &bin_attr_mem); | 
|  |  | 
|  | if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) | 
|  | return; | 
|  |  | 
|  | device_remove_bin_file(dev, &bin_attr_pci_config); | 
|  | device_remove_file(dev, &dev_attr_beacon); | 
|  | if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) | 
|  | return; | 
|  | device_remove_bin_file(dev, &bin_attr_esw_config); | 
|  | if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC) | 
|  | return; | 
|  | device_remove_bin_file(dev, &bin_attr_npar_config); | 
|  | device_remove_bin_file(dev, &bin_attr_pm_config); | 
|  | device_remove_bin_file(dev, &bin_attr_esw_stats); | 
|  | } | 
|  |  | 
|  | void qlcnic_82xx_add_sysfs(struct qlcnic_adapter *adapter) | 
|  | { | 
|  | qlcnic_create_diag_entries(adapter); | 
|  | } | 
|  |  | 
|  | void qlcnic_82xx_remove_sysfs(struct qlcnic_adapter *adapter) | 
|  | { | 
|  | qlcnic_remove_diag_entries(adapter); | 
|  | } | 
|  |  | 
|  | void qlcnic_83xx_add_sysfs(struct qlcnic_adapter *adapter) | 
|  | { | 
|  | struct device *dev = &adapter->pdev->dev; | 
|  |  | 
|  | qlcnic_create_diag_entries(adapter); | 
|  |  | 
|  | if (sysfs_create_bin_file(&dev->kobj, &bin_attr_flash)) | 
|  | dev_info(dev, "failed to create flash sysfs entry\n"); | 
|  | } | 
|  |  | 
|  | void qlcnic_83xx_remove_sysfs(struct qlcnic_adapter *adapter) | 
|  | { | 
|  | struct device *dev = &adapter->pdev->dev; | 
|  |  | 
|  | qlcnic_remove_diag_entries(adapter); | 
|  | sysfs_remove_bin_file(&dev->kobj, &bin_attr_flash); | 
|  | } |