diff options
Diffstat (limited to 'drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c')
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c | 1431 |
1 files changed, 1431 insertions, 0 deletions
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c new file mode 100644 index 000000000..5c2edb715 --- /dev/null +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -0,0 +1,1431 @@ +// 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/aer.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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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 = qlcnic_sysfs_read_crb, + .write = qlcnic_sysfs_write_crb, +}; + +static const struct bin_attribute bin_attr_mem = { + .attr = { .name = "mem", .mode = 0644 }, + .size = 0, + .read = qlcnic_sysfs_read_mem, + .write = qlcnic_sysfs_write_mem, +}; + +static const struct bin_attribute bin_attr_npar_config = { + .attr = { .name = "npar_config", .mode = 0644 }, + .size = 0, + .read = qlcnic_sysfs_read_npar_config, + .write = qlcnic_sysfs_write_npar_config, +}; + +static const struct bin_attribute bin_attr_pci_config = { + .attr = { .name = "pci_config", .mode = 0644 }, + .size = 0, + .read = qlcnic_sysfs_read_pci_config, + .write = NULL, +}; + +static const struct bin_attribute bin_attr_port_stats = { + .attr = { .name = "port_stats", .mode = 0644 }, + .size = 0, + .read = qlcnic_sysfs_get_port_stats, + .write = qlcnic_sysfs_clear_port_stats, +}; + +static const struct bin_attribute bin_attr_esw_stats = { + .attr = { .name = "esw_stats", .mode = 0644 }, + .size = 0, + .read = qlcnic_sysfs_get_esw_stats, + .write = qlcnic_sysfs_clear_esw_stats, +}; + +static const struct bin_attribute bin_attr_esw_config = { + .attr = { .name = "esw_config", .mode = 0644 }, + .size = 0, + .read = qlcnic_sysfs_read_esw_config, + .write = qlcnic_sysfs_write_esw_config, +}; + +static const struct bin_attribute bin_attr_pm_config = { + .attr = { .name = "pm_config", .mode = 0644 }, + .size = 0, + .read = qlcnic_sysfs_read_pm_config, + .write = qlcnic_sysfs_write_pm_config, +}; + +static const struct bin_attribute bin_attr_flash = { + .attr = { .name = "flash", .mode = 0644 }, + .size = 0, + .read = qlcnic_83xx_sysfs_flash_read_handler, + .write = 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); +} |