linux_dsm_epyc7002/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
Vladimir Zapolskiy d7a32b6e6b net: qlcnic: clean up sysfs error codes
Replace confusing QL_STATUS_INVALID_PARAM == -1 == -EPERM with -EINVAL
and QLC_STATUS_UNSUPPORTED_CMD == -2 == -ENOENT with -EOPNOTSUPP, the
latter error code is arguable, but it is already used in the driver,
so let it be here as well.

Also remove always false (!buf) check on read(), the driver should
not care if userspace gets its EFAULT or not.

Signed-off-by: Vladimir Zapolskiy <vz@mleia.com>
Acked-by: Rajesh Borundia <rajesh.borundia@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-05-28 20:14:45 -07:00

1432 lines
36 KiB
C

/*
* QLogic qlcnic NIC Driver
* Copyright (c) 2009-2013 QLogic Corporation
*
* See LICENSE.qlcnic for copyright and licensing details.
*/
#include <linux/slab.h>
#include <linux/interrupt.h>
#include "qlcnic.h"
#include "qlcnic_hw.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
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 = container_of(kobj, struct device, 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 = container_of(kobj, struct device, 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 = container_of(kobj, struct device, 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 = container_of(kobj, struct device, 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 = container_of(kobj, struct device, 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 = container_of(kobj, struct device, 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 = container_of(kobj, struct device, 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 = container_of(kobj, struct device, 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 = container_of(kobj, struct device, 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 = container_of(kobj, struct device, 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 = container_of(kobj, struct device, 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 = container_of(kobj, struct device, 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 = container_of(kobj, struct device, 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 = container_of(kobj, struct device, 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 = container_of(kobj, struct device, 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 = container_of(kobj, struct device, 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 = container_of(kobj, struct device, kobj);
struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
ret = kstrtoul(buf, 16, &data);
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 struct device_attribute dev_attr_bridged_mode = {
.attr = {.name = "bridged_mode", .mode = (S_IRUGO | S_IWUSR)},
.show = qlcnic_show_bridged_mode,
.store = qlcnic_store_bridged_mode,
};
static struct device_attribute dev_attr_diag_mode = {
.attr = {.name = "diag_mode", .mode = (S_IRUGO | S_IWUSR)},
.show = qlcnic_show_diag_mode,
.store = qlcnic_store_diag_mode,
};
static struct device_attribute dev_attr_beacon = {
.attr = {.name = "beacon", .mode = (S_IRUGO | S_IWUSR)},
.show = qlcnic_show_beacon,
.store = qlcnic_store_beacon,
};
static struct bin_attribute bin_attr_crb = {
.attr = {.name = "crb", .mode = (S_IRUGO | S_IWUSR)},
.size = 0,
.read = qlcnic_sysfs_read_crb,
.write = qlcnic_sysfs_write_crb,
};
static struct bin_attribute bin_attr_mem = {
.attr = {.name = "mem", .mode = (S_IRUGO | S_IWUSR)},
.size = 0,
.read = qlcnic_sysfs_read_mem,
.write = qlcnic_sysfs_write_mem,
};
static struct bin_attribute bin_attr_npar_config = {
.attr = {.name = "npar_config", .mode = (S_IRUGO | S_IWUSR)},
.size = 0,
.read = qlcnic_sysfs_read_npar_config,
.write = qlcnic_sysfs_write_npar_config,
};
static struct bin_attribute bin_attr_pci_config = {
.attr = {.name = "pci_config", .mode = (S_IRUGO | S_IWUSR)},
.size = 0,
.read = qlcnic_sysfs_read_pci_config,
.write = NULL,
};
static struct bin_attribute bin_attr_port_stats = {
.attr = {.name = "port_stats", .mode = (S_IRUGO | S_IWUSR)},
.size = 0,
.read = qlcnic_sysfs_get_port_stats,
.write = qlcnic_sysfs_clear_port_stats,
};
static struct bin_attribute bin_attr_esw_stats = {
.attr = {.name = "esw_stats", .mode = (S_IRUGO | S_IWUSR)},
.size = 0,
.read = qlcnic_sysfs_get_esw_stats,
.write = qlcnic_sysfs_clear_esw_stats,
};
static struct bin_attribute bin_attr_esw_config = {
.attr = {.name = "esw_config", .mode = (S_IRUGO | S_IWUSR)},
.size = 0,
.read = qlcnic_sysfs_read_esw_config,
.write = qlcnic_sysfs_write_esw_config,
};
static struct bin_attribute bin_attr_pm_config = {
.attr = {.name = "pm_config", .mode = (S_IRUGO | S_IWUSR)},
.size = 0,
.read = qlcnic_sysfs_read_pm_config,
.write = qlcnic_sysfs_write_pm_config,
};
static struct bin_attribute bin_attr_flash = {
.attr = {.name = "flash", .mode = (S_IRUGO | S_IWUSR)},
.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, S_IRUGO,
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);
}