Commit 4e8acb01 authored by Rajesh Borundia's avatar Rajesh Borundia Committed by David S. Miller

qlcnic: configure port on eswitch

o Nic partition capable devices has embedded switch, this needs to support
various features like external switch.
Signed-off-by: default avatarRajesh Borundia <rajesh.borundia@qlogic.com>
Signed-off-by: default avatarAmit Kumar Salecha <amit.salecha@qlogic.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 251b036a
......@@ -556,6 +556,7 @@ struct qlcnic_recv_context {
#define QLCNIC_CDRP_CMD_GET_ESWITCH_STATUS 0x00000026
#define QLCNIC_CDRP_CMD_SET_PORTMIRRORING 0x00000027
#define QLCNIC_CDRP_CMD_CONFIGURE_ESWITCH 0x00000028
#define QLCNIC_CDRP_CMD_GET_ESWITCH_PORT_CONFIG 0x00000029
#define QLCNIC_CDRP_CMD_GET_ESWITCH_STATS 0x0000002a
#define QLCNIC_RCODE_SUCCESS 0
......@@ -1044,7 +1045,7 @@ struct qlcnic_pci_info {
};
struct qlcnic_npar_info {
u16 vlan_id;
u16 pvid;
u16 min_bw;
u16 max_bw;
u8 phy_port;
......@@ -1052,11 +1053,13 @@ struct qlcnic_npar_info {
u8 active;
u8 enable_pm;
u8 dest_npar;
u8 host_vlan_tag;
u8 promisc_mode;
u8 discard_tagged;
u8 mac_learning;
u8 mac_anti_spoof;
u8 promisc_mode;
u8 offload_flags;
};
struct qlcnic_eswitch {
u8 port;
u8 active_vports;
......@@ -1088,7 +1091,6 @@ struct qlcnic_eswitch {
#define IS_VALID_BW(bw) (bw >= MIN_BW && bw <= MAX_BW)
#define IS_VALID_TX_QUEUES(que) (que > 0 && que <= MAX_TX_QUEUES)
#define IS_VALID_RX_QUEUES(que) (que > 0 && que <= MAX_RX_QUEUES)
#define IS_VALID_MODE(mode) (mode == 0 || mode == 1)
struct qlcnic_pci_func_cfg {
u16 func_type;
......@@ -1120,12 +1122,16 @@ struct qlcnic_pm_func_cfg {
struct qlcnic_esw_func_cfg {
u16 vlan_id;
u8 op_mode;
u8 op_type;
u8 pci_func;
u8 host_vlan_tag;
u8 promisc_mode;
u8 discard_tagged;
u8 mac_learning;
u8 reserved;
u8 mac_anti_spoof;
u8 offload_flags;
u8 reserved[5];
};
#define QLCNIC_STATS_VERSION 1
......@@ -1276,8 +1282,10 @@ int qlcnic_get_eswitch_capabilities(struct qlcnic_adapter *, u8,
int qlcnic_get_eswitch_status(struct qlcnic_adapter *, u8,
struct qlcnic_eswitch *);
int qlcnic_toggle_eswitch(struct qlcnic_adapter *, u8, u8);
int qlcnic_config_switch_port(struct qlcnic_adapter *, u8, int, u8, u8,
u8, u8, u16);
int qlcnic_config_switch_port(struct qlcnic_adapter *,
struct qlcnic_esw_func_cfg *);
int qlcnic_get_eswitch_port_config(struct qlcnic_adapter *,
struct qlcnic_esw_func_cfg *);
int qlcnic_config_port_mirroring(struct qlcnic_adapter *, u8, u8, u8);
int qlcnic_get_port_stats(struct qlcnic_adapter *, const u8, const u8,
struct __qlcnic_esw_statistics *);
......
......@@ -813,9 +813,8 @@ int qlcnic_get_eswitch_capabilities(struct qlcnic_adapter *adapter, u8 port,
arg2 = QLCRD32(adapter, QLCNIC_ARG2_CRB_OFFSET);
eswitch->port = arg1 & 0xf;
eswitch->active_vports = LSB(arg2);
eswitch->max_ucast_filters = MSB(arg2);
eswitch->max_active_vlans = LSB(MSW(arg2));
eswitch->max_ucast_filters = LSW(arg2);
eswitch->max_active_vlans = MSW(arg2) & 0xfff;
if (arg1 & BIT_6)
eswitch->flags |= QLCNIC_SWITCH_VLAN_FILTERING;
if (arg1 & BIT_7)
......@@ -943,47 +942,6 @@ int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id,
return err;
}
/* Configure eSwitch port */
int qlcnic_config_switch_port(struct qlcnic_adapter *adapter, u8 id,
int vlan_tagging, u8 discard_tagged, u8 promsc_mode,
u8 mac_learn, u8 pci_func, u16 vlan_id)
{
int err = -EIO;
u32 arg1;
struct qlcnic_eswitch *eswitch;
if (adapter->op_mode != QLCNIC_MGMT_FUNC)
return err;
eswitch = &adapter->eswitch[id];
if (!(eswitch->flags & QLCNIC_SWITCH_ENABLE))
return err;
arg1 = eswitch->port | (discard_tagged ? BIT_4 : 0);
arg1 |= (promsc_mode ? BIT_6 : 0) | (mac_learn ? BIT_7 : 0);
arg1 |= pci_func << 8;
if (vlan_tagging)
arg1 |= BIT_5 | (vlan_id << 16);
err = qlcnic_issue_cmd(adapter,
adapter->ahw.pci_func,
adapter->fw_hal_version,
arg1,
0,
0,
QLCNIC_CDRP_CMD_CONFIGURE_ESWITCH);
if (err != QLCNIC_RCODE_SUCCESS) {
dev_err(&adapter->pdev->dev,
"Failed to configure eswitch port%d\n", eswitch->port);
} else {
dev_info(&adapter->pdev->dev,
"Configured eSwitch for port %d\n", eswitch->port);
}
return err;
}
int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func,
const u8 rx_tx, struct __qlcnic_esw_statistics *esw_stats) {
......@@ -1108,3 +1066,131 @@ int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, const u8 func_esw,
"rx_ctx=%d\n", func_esw, port, rx_tx);
return -EIO;
}
static int
__qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter,
u32 *arg1, u32 *arg2)
{
int err = -EIO;
u8 pci_func;
pci_func = (*arg1 >> 8);
err = qlcnic_issue_cmd(adapter,
adapter->ahw.pci_func,
adapter->fw_hal_version,
*arg1,
0,
0,
QLCNIC_CDRP_CMD_GET_ESWITCH_PORT_CONFIG);
if (err == QLCNIC_RCODE_SUCCESS) {
*arg1 = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET);
*arg2 = QLCRD32(adapter, QLCNIC_ARG2_CRB_OFFSET);
dev_info(&adapter->pdev->dev,
"eSwitch port config for pci func%d\n", pci_func);
} else {
dev_err(&adapter->pdev->dev,
"Failed to get eswitch port config%d\n", pci_func);
}
return err;
}
/* Configure eSwitch port
op_mode = 0 for setting default port behavior
op_mode = 1 for setting vlan id
op_mode = 2 for deleting vlan id
op_type = 0 for vlan_id
op_type = 1 for port vlan_id
*/
int qlcnic_config_switch_port(struct qlcnic_adapter *adapter,
struct qlcnic_esw_func_cfg *esw_cfg)
{
int err = -EIO;
u32 arg1, arg2 = 0;
u8 pci_func;
if (adapter->op_mode != QLCNIC_MGMT_FUNC)
return err;
pci_func = esw_cfg->pci_func;
arg1 = (adapter->npars[pci_func].phy_port & BIT_0);
arg1 |= (pci_func << 8);
if (__qlcnic_get_eswitch_port_config(adapter, &arg1, &arg2))
return err;
arg1 &= ~(0x0ff << 8);
arg1 |= (pci_func << 8);
arg1 &= ~(BIT_2 | BIT_3);
switch (esw_cfg->op_mode) {
case QLCNIC_PORT_DEFAULTS:
arg1 |= (BIT_4 | BIT_6 | BIT_7);
arg2 |= (BIT_0 | BIT_1);
if (adapter->capabilities & QLCNIC_FW_CAPABILITY_TSO)
arg2 |= (BIT_2 | BIT_3);
if (!(esw_cfg->discard_tagged))
arg1 &= ~BIT_4;
if (!(esw_cfg->promisc_mode))
arg1 &= ~BIT_6;
if (!(esw_cfg->mac_learning))
arg1 &= ~BIT_7;
if (!(esw_cfg->mac_anti_spoof))
arg2 &= ~BIT_0;
if (!(esw_cfg->offload_flags & BIT_0))
arg2 &= ~(BIT_1 | BIT_2 | BIT_3);
if (!(esw_cfg->offload_flags & BIT_1))
arg2 &= ~BIT_2;
if (!(esw_cfg->offload_flags & BIT_2))
arg2 &= ~BIT_3;
break;
case QLCNIC_ADD_VLAN:
arg1 |= (BIT_2 | BIT_5);
arg1 |= (esw_cfg->vlan_id << 16);
break;
case QLCNIC_DEL_VLAN:
arg1 |= (BIT_3 | BIT_5);
arg1 &= ~(0x0ffff << 16);
default:
return err;
}
err = qlcnic_issue_cmd(adapter,
adapter->ahw.pci_func,
adapter->fw_hal_version,
arg1,
arg2,
0,
QLCNIC_CDRP_CMD_CONFIGURE_ESWITCH);
if (err != QLCNIC_RCODE_SUCCESS) {
dev_err(&adapter->pdev->dev,
"Failed to configure eswitch port%d\n", pci_func);
} else {
dev_info(&adapter->pdev->dev,
"Configured eSwitch for port %d\n", pci_func);
}
return err;
}
int
qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter,
struct qlcnic_esw_func_cfg *esw_cfg)
{
u32 arg1, arg2;
u8 phy_port;
if (adapter->op_mode == QLCNIC_MGMT_FUNC)
phy_port = adapter->npars[esw_cfg->pci_func].phy_port;
else
phy_port = adapter->physical_port;
arg1 = phy_port;
arg1 |= (esw_cfg->pci_func << 8);
if (__qlcnic_get_eswitch_port_config(adapter, &arg1, &arg2))
return -EIO;
esw_cfg->discard_tagged = !!(arg1 & BIT_4);
esw_cfg->host_vlan_tag = !!(arg1 & BIT_5);
esw_cfg->promisc_mode = !!(arg1 & BIT_6);
esw_cfg->mac_learning = !!(arg1 & BIT_7);
esw_cfg->vlan_id = LSW(arg1 >> 16);
esw_cfg->mac_anti_spoof = (arg2 & 0x1);
esw_cfg->offload_flags = ((arg2 >> 1) & 0x7);
return 0;
}
......@@ -775,6 +775,7 @@ struct qlcnic_legacy_intr_set {
#define QLCNIC_DRV_OP_MODE 0x1b2170
#define QLCNIC_MSIX_BASE 0x132110
#define QLCNIC_MAX_PCI_FUNC 8
#define QLCNIC_MAX_VLAN_FILTERS 64
/* PCI function operational mode */
enum {
......@@ -783,6 +784,12 @@ enum {
QLCNIC_NON_PRIV_FUNC = 2
};
enum {
QLCNIC_PORT_DEFAULTS = 0,
QLCNIC_ADD_VLAN = 1,
QLCNIC_DEL_VLAN = 2
};
#define QLC_DEV_DRV_DEFAULT 0x11111111
#define LSB(x) ((uint8_t)(x))
......
......@@ -506,7 +506,6 @@ qlcnic_init_pci_info(struct qlcnic_adapter *adapter)
adapter->npars[pfn].active = pci_info[i].active;
adapter->npars[pfn].type = pci_info[i].type;
adapter->npars[pfn].phy_port = pci_info[i].default_port;
adapter->npars[pfn].mac_learning = DEFAULT_MAC_LEARN;
adapter->npars[pfn].min_bw = pci_info[i].tx_min_bw;
adapter->npars[pfn].max_bw = pci_info[i].tx_max_bw;
}
......@@ -757,18 +756,40 @@ qlcnic_check_options(struct qlcnic_adapter *adapter)
adapter->max_rds_rings = MAX_RDS_RINGS;
}
static int
qlcnic_reset_eswitch_config(struct qlcnic_adapter *adapter,
struct qlcnic_npar_info *npar, int pci_func)
{
struct qlcnic_esw_func_cfg esw_cfg;
esw_cfg.op_mode = QLCNIC_PORT_DEFAULTS;
esw_cfg.pci_func = pci_func;
esw_cfg.vlan_id = npar->pvid;
esw_cfg.mac_learning = npar->mac_learning;
esw_cfg.discard_tagged = npar->discard_tagged;
esw_cfg.mac_anti_spoof = npar->mac_anti_spoof;
esw_cfg.offload_flags = npar->offload_flags;
esw_cfg.promisc_mode = npar->promisc_mode;
if (qlcnic_config_switch_port(adapter, &esw_cfg))
return -EIO;
esw_cfg.op_mode = QLCNIC_ADD_VLAN;
if (qlcnic_config_switch_port(adapter, &esw_cfg))
return -EIO;
return 0;
}
static int
qlcnic_reset_npar_config(struct qlcnic_adapter *adapter)
{
int i, err = 0;
int i, err;
struct qlcnic_npar_info *npar;
struct qlcnic_info nic_info;
if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
!adapter->need_fw_reset)
!adapter->need_fw_reset || adapter->op_mode != QLCNIC_MGMT_FUNC)
return 0;
if (adapter->op_mode == QLCNIC_MGMT_FUNC) {
/* Set the NPAR config data after FW reset */
for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) {
npar = &adapter->npars[i];
......@@ -776,29 +797,24 @@ qlcnic_reset_npar_config(struct qlcnic_adapter *adapter)
continue;
err = qlcnic_get_nic_info(adapter, &nic_info, i);
if (err)
goto err_out;
return err;
nic_info.min_tx_bw = npar->min_bw;
nic_info.max_tx_bw = npar->max_bw;
err = qlcnic_set_nic_info(adapter, &nic_info);
if (err)
goto err_out;
return err;
if (npar->enable_pm) {
err = qlcnic_config_port_mirroring(adapter,
npar->dest_npar, 1, i);
if (err)
goto err_out;
}
npar->mac_learning = DEFAULT_MAC_LEARN;
npar->host_vlan_tag = 0;
npar->promisc_mode = 0;
npar->discard_tagged = 0;
npar->vlan_id = 0;
}
return err;
}
err_out:
err = qlcnic_reset_eswitch_config(adapter, npar, i);
if (err)
return err;
}
return 0;
}
static int
......@@ -863,12 +879,10 @@ qlcnic_start_firmware(struct qlcnic_adapter *adapter)
QLCWR32(adapter, QLCNIC_CRB_DEV_STATE, QLCNIC_DEV_READY);
qlcnic_idc_debug_info(adapter, 1);
qlcnic_check_options(adapter);
if (qlcnic_reset_npar_config(adapter))
goto err_out;
qlcnic_dev_set_npar_ready(adapter);
qlcnic_check_options(adapter);
adapter->need_fw_reset = 0;
qlcnic_release_firmware(adapter);
......@@ -3082,9 +3096,6 @@ validate_pm_config(struct qlcnic_adapter *adapter,
if (adapter->npars[dest_pci_func].type != QLCNIC_TYPE_NIC)
return QL_STATUS_INVALID_PARAM;
if (!IS_VALID_MODE(pm_cfg[i].action))
return QL_STATUS_INVALID_PARAM;
s_esw_id = adapter->npars[src_pci_func].phy_port;
d_esw_id = adapter->npars[dest_pci_func].phy_port;
......@@ -3118,7 +3129,7 @@ qlcnic_sysfs_write_pm_config(struct file *filp, struct kobject *kobj,
return ret;
for (i = 0; i < count; i++) {
pci_func = pm_cfg[i].pci_func;
action = pm_cfg[i].action;
action = !!pm_cfg[i].action;
id = adapter->npars[pci_func].phy_port;
ret = qlcnic_config_port_mirroring(adapter, id,
action, pci_func);
......@@ -3129,7 +3140,7 @@ qlcnic_sysfs_write_pm_config(struct file *filp, struct kobject *kobj,
for (i = 0; i < count; i++) {
pci_func = pm_cfg[i].pci_func;
id = adapter->npars[pci_func].phy_port;
adapter->npars[pci_func].enable_pm = pm_cfg[i].action;
adapter->npars[pci_func].enable_pm = !!pm_cfg[i].action;
adapter->npars[pci_func].dest_npar = id;
}
return size;
......@@ -3165,26 +3176,34 @@ validate_esw_config(struct qlcnic_adapter *adapter,
{
u8 pci_func;
int i;
for (i = 0; i < count; i++) {
pci_func = esw_cfg[i].pci_func;
if (pci_func >= QLCNIC_MAX_PCI_FUNC)
return QL_STATUS_INVALID_PARAM;
if (adapter->npars[i].type != QLCNIC_TYPE_NIC)
if (adapter->op_mode == QLCNIC_MGMT_FUNC)
if (adapter->npars[pci_func].type != QLCNIC_TYPE_NIC)
return QL_STATUS_INVALID_PARAM;
if (esw_cfg->host_vlan_tag == 1)
switch (esw_cfg[i].op_mode) {
case QLCNIC_PORT_DEFAULTS:
break;
case QLCNIC_ADD_VLAN:
if (!IS_VALID_VLAN(esw_cfg[i].vlan_id))
return QL_STATUS_INVALID_PARAM;
if (!IS_VALID_MODE(esw_cfg[i].promisc_mode)
|| !IS_VALID_MODE(esw_cfg[i].host_vlan_tag)
|| !IS_VALID_MODE(esw_cfg[i].mac_learning)
|| !IS_VALID_MODE(esw_cfg[i].discard_tagged))
if (!esw_cfg[i].op_type)
return QL_STATUS_INVALID_PARAM;
break;
case QLCNIC_DEL_VLAN:
if (!IS_VALID_VLAN(esw_cfg[i].vlan_id))
return QL_STATUS_INVALID_PARAM;
if (!esw_cfg[i].op_type)
return QL_STATUS_INVALID_PARAM;
break;
default:
return QL_STATUS_INVALID_PARAM;
}
}
return 0;
}
......@@ -3195,8 +3214,9 @@ qlcnic_sysfs_write_esw_config(struct file *file, struct kobject *kobj,
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;
u8 id, pci_func;
u8 pci_func;
count = size / sizeof(struct qlcnic_esw_func_cfg);
rem = size % sizeof(struct qlcnic_esw_func_cfg);
......@@ -3209,28 +3229,28 @@ qlcnic_sysfs_write_esw_config(struct file *file, struct kobject *kobj,
return ret;
for (i = 0; i < count; i++) {
pci_func = esw_cfg[i].pci_func;
id = adapter->npars[pci_func].phy_port;
ret = qlcnic_config_switch_port(adapter, id,
esw_cfg[i].host_vlan_tag,
esw_cfg[i].discard_tagged,
esw_cfg[i].promisc_mode,
esw_cfg[i].mac_learning,
esw_cfg[i].pci_func,
esw_cfg[i].vlan_id);
if (ret)
return ret;
if (qlcnic_config_switch_port(adapter, &esw_cfg[i]))
return QL_STATUS_INVALID_PARAM;
}
for (i = 0; i < count; i++) {
pci_func = esw_cfg[i].pci_func;
adapter->npars[pci_func].promisc_mode = esw_cfg[i].promisc_mode;
adapter->npars[pci_func].mac_learning = esw_cfg[i].mac_learning;
adapter->npars[pci_func].vlan_id = esw_cfg[i].vlan_id;
adapter->npars[pci_func].discard_tagged =
esw_cfg[i].discard_tagged;
adapter->npars[pci_func].host_vlan_tag =
esw_cfg[i].host_vlan_tag;
npar = &adapter->npars[pci_func];
switch (esw_cfg[i].op_mode) {
case QLCNIC_PORT_DEFAULTS:
npar->promisc_mode = esw_cfg[i].promisc_mode;
npar->mac_learning = esw_cfg[i].mac_learning;
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;
}
}
return size;
......@@ -3243,7 +3263,7 @@ qlcnic_sysfs_read_esw_config(struct file *file, struct kobject *kobj,
struct device *dev = container_of(kobj, struct device, kobj);
struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
struct qlcnic_esw_func_cfg esw_cfg[QLCNIC_MAX_PCI_FUNC];
int i;
u8 i;
if (size != sizeof(esw_cfg))
return QL_STATUS_INVALID_PARAM;
......@@ -3251,12 +3271,9 @@ qlcnic_sysfs_read_esw_config(struct file *file, struct kobject *kobj,
for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) {
if (adapter->npars[i].type != QLCNIC_TYPE_NIC)
continue;
esw_cfg[i].host_vlan_tag = adapter->npars[i].host_vlan_tag;
esw_cfg[i].promisc_mode = adapter->npars[i].promisc_mode;
esw_cfg[i].discard_tagged = adapter->npars[i].discard_tagged;
esw_cfg[i].vlan_id = adapter->npars[i].vlan_id;
esw_cfg[i].mac_learning = adapter->npars[i].mac_learning;
esw_cfg[i].pci_func = i;
if (qlcnic_get_eswitch_port_config(adapter, &esw_cfg[i]))
return QL_STATUS_INVALID_PARAM;
}
memcpy(buf, &esw_cfg, size);
......@@ -3580,15 +3597,16 @@ qlcnic_create_diag_entries(struct qlcnic_adapter *adapter)
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 (!(adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
adapter->op_mode != QLCNIC_MGMT_FUNC)
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->op_mode != QLCNIC_MGMT_FUNC)
return;
if (device_create_bin_file(dev, &bin_attr_pci_config))
dev_info(dev, "failed to create pci config sysfs entry");
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_esw_config))
dev_info(dev, "failed to create esw 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))
......@@ -3607,12 +3625,13 @@ qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter)
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 (!(adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
adapter->op_mode != QLCNIC_MGMT_FUNC)
if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED))
return;
device_remove_bin_file(dev, &bin_attr_esw_config);
if (adapter->op_mode != QLCNIC_MGMT_FUNC)
return;
device_remove_bin_file(dev, &bin_attr_pci_config);
device_remove_bin_file(dev, &bin_attr_npar_config);
device_remove_bin_file(dev, &bin_attr_esw_config);
device_remove_bin_file(dev, &bin_attr_pm_config);
device_remove_bin_file(dev, &bin_attr_esw_stats);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment