diff options
Diffstat (limited to 'drivers/net/ethernet/huawei/hinic/hinic_port.c')
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_port.c | 475 |
1 files changed, 465 insertions, 10 deletions
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c index c9aedecd19c9..c07adf793215 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c @@ -1,16 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * */ #include <linux/types.h> @@ -553,3 +544,467 @@ int hinic_set_rx_lro_state(struct hinic_dev *nic_dev, u8 lro_en, return 0; } + +int hinic_rss_set_indir_tbl(struct hinic_dev *nic_dev, u32 tmpl_idx, + const u32 *indir_table) +{ + struct hinic_rss_indirect_tbl *indir_tbl; + struct hinic_func_to_io *func_to_io; + struct hinic_cmdq_buf cmd_buf; + struct hinic_hwdev *hwdev; + struct hinic_hwif *hwif; + struct pci_dev *pdev; + u32 indir_size; + u64 out_param; + int err, i; + u32 *temp; + + hwdev = nic_dev->hwdev; + func_to_io = &hwdev->func_to_io; + hwif = hwdev->hwif; + pdev = hwif->pdev; + + err = hinic_alloc_cmdq_buf(&func_to_io->cmdqs, &cmd_buf); + if (err) { + dev_err(&pdev->dev, "Failed to allocate cmdq buf\n"); + return err; + } + + cmd_buf.size = sizeof(*indir_tbl); + + indir_tbl = cmd_buf.buf; + indir_tbl->group_index = cpu_to_be32(tmpl_idx); + + for (i = 0; i < HINIC_RSS_INDIR_SIZE; i++) { + indir_tbl->entry[i] = indir_table[i]; + + if (0x3 == (i & 0x3)) { + temp = (u32 *)&indir_tbl->entry[i - 3]; + *temp = cpu_to_be32(*temp); + } + } + + /* cfg the rss indirect table by command queue */ + indir_size = HINIC_RSS_INDIR_SIZE / 2; + indir_tbl->offset = 0; + indir_tbl->size = cpu_to_be32(indir_size); + + err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC, + HINIC_UCODE_CMD_SET_RSS_INDIR_TABLE, + &cmd_buf, &out_param); + if (err || out_param != 0) { + dev_err(&pdev->dev, "Failed to set rss indir table\n"); + err = -EFAULT; + goto free_buf; + } + + indir_tbl->offset = cpu_to_be32(indir_size); + indir_tbl->size = cpu_to_be32(indir_size); + memcpy(&indir_tbl->entry[0], &indir_tbl->entry[indir_size], indir_size); + + err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC, + HINIC_UCODE_CMD_SET_RSS_INDIR_TABLE, + &cmd_buf, &out_param); + if (err || out_param != 0) { + dev_err(&pdev->dev, "Failed to set rss indir table\n"); + err = -EFAULT; + } + +free_buf: + hinic_free_cmdq_buf(&func_to_io->cmdqs, &cmd_buf); + + return err; +} + +int hinic_rss_get_indir_tbl(struct hinic_dev *nic_dev, u32 tmpl_idx, + u32 *indir_table) +{ + struct hinic_rss_indir_table rss_cfg = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size = sizeof(rss_cfg); + int err = 0, i; + + rss_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif); + rss_cfg.template_id = tmpl_idx; + + err = hinic_port_msg_cmd(hwdev, + HINIC_PORT_CMD_GET_RSS_TEMPLATE_INDIR_TBL, + &rss_cfg, sizeof(rss_cfg), &rss_cfg, + &out_size); + if (err || !out_size || rss_cfg.status) { + dev_err(&pdev->dev, "Failed to get indir table, err: %d, status: 0x%x, out size: 0x%x\n", + err, rss_cfg.status, out_size); + return -EINVAL; + } + + hinic_be32_to_cpu(rss_cfg.indir, HINIC_RSS_INDIR_SIZE); + for (i = 0; i < HINIC_RSS_INDIR_SIZE; i++) + indir_table[i] = rss_cfg.indir[i]; + + return 0; +} + +int hinic_set_rss_type(struct hinic_dev *nic_dev, u32 tmpl_idx, + struct hinic_rss_type rss_type) +{ + struct hinic_rss_context_tbl *ctx_tbl; + struct hinic_func_to_io *func_to_io; + struct hinic_cmdq_buf cmd_buf; + struct hinic_hwdev *hwdev; + struct hinic_hwif *hwif; + struct pci_dev *pdev; + u64 out_param; + u32 ctx = 0; + int err; + + hwdev = nic_dev->hwdev; + func_to_io = &hwdev->func_to_io; + hwif = hwdev->hwif; + pdev = hwif->pdev; + + err = hinic_alloc_cmdq_buf(&func_to_io->cmdqs, &cmd_buf); + if (err) { + dev_err(&pdev->dev, "Failed to allocate cmd buf\n"); + return -ENOMEM; + } + + ctx |= HINIC_RSS_TYPE_SET(1, VALID) | + HINIC_RSS_TYPE_SET(rss_type.ipv4, IPV4) | + HINIC_RSS_TYPE_SET(rss_type.ipv6, IPV6) | + HINIC_RSS_TYPE_SET(rss_type.ipv6_ext, IPV6_EXT) | + HINIC_RSS_TYPE_SET(rss_type.tcp_ipv4, TCP_IPV4) | + HINIC_RSS_TYPE_SET(rss_type.tcp_ipv6, TCP_IPV6) | + HINIC_RSS_TYPE_SET(rss_type.tcp_ipv6_ext, TCP_IPV6_EXT) | + HINIC_RSS_TYPE_SET(rss_type.udp_ipv4, UDP_IPV4) | + HINIC_RSS_TYPE_SET(rss_type.udp_ipv6, UDP_IPV6); + + cmd_buf.size = sizeof(struct hinic_rss_context_tbl); + + ctx_tbl = (struct hinic_rss_context_tbl *)cmd_buf.buf; + ctx_tbl->group_index = cpu_to_be32(tmpl_idx); + ctx_tbl->offset = 0; + ctx_tbl->size = sizeof(u32); + ctx_tbl->size = cpu_to_be32(ctx_tbl->size); + ctx_tbl->rsvd = 0; + ctx_tbl->ctx = cpu_to_be32(ctx); + + /* cfg the rss context table by command queue */ + err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC, + HINIC_UCODE_CMD_SET_RSS_CONTEXT_TABLE, + &cmd_buf, &out_param); + + hinic_free_cmdq_buf(&func_to_io->cmdqs, &cmd_buf); + + if (err || out_param != 0) { + dev_err(&pdev->dev, "Failed to set rss context table, err: %d\n", + err); + return -EFAULT; + } + + return 0; +} + +int hinic_get_rss_type(struct hinic_dev *nic_dev, u32 tmpl_idx, + struct hinic_rss_type *rss_type) +{ + struct hinic_rss_context_table ctx_tbl = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif; + struct pci_dev *pdev; + u16 out_size = sizeof(ctx_tbl); + int err; + + if (!hwdev || !rss_type) + return -EINVAL; + + hwif = hwdev->hwif; + pdev = hwif->pdev; + + ctx_tbl.func_id = HINIC_HWIF_FUNC_IDX(hwif); + ctx_tbl.template_id = tmpl_idx; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_RSS_CTX_TBL, + &ctx_tbl, sizeof(ctx_tbl), + &ctx_tbl, &out_size); + if (err || !out_size || ctx_tbl.status) { + dev_err(&pdev->dev, "Failed to get hash type, err: %d, status: 0x%x, out size: 0x%x\n", + err, ctx_tbl.status, out_size); + return -EINVAL; + } + + rss_type->ipv4 = HINIC_RSS_TYPE_GET(ctx_tbl.context, IPV4); + rss_type->ipv6 = HINIC_RSS_TYPE_GET(ctx_tbl.context, IPV6); + rss_type->ipv6_ext = HINIC_RSS_TYPE_GET(ctx_tbl.context, IPV6_EXT); + rss_type->tcp_ipv4 = HINIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV4); + rss_type->tcp_ipv6 = HINIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV6); + rss_type->tcp_ipv6_ext = HINIC_RSS_TYPE_GET(ctx_tbl.context, + TCP_IPV6_EXT); + rss_type->udp_ipv4 = HINIC_RSS_TYPE_GET(ctx_tbl.context, UDP_IPV4); + rss_type->udp_ipv6 = HINIC_RSS_TYPE_GET(ctx_tbl.context, UDP_IPV6); + + return 0; +} + +int hinic_rss_set_template_tbl(struct hinic_dev *nic_dev, u32 template_id, + const u8 *temp) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct hinic_rss_key rss_key = { 0 }; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + rss_key.func_id = HINIC_HWIF_FUNC_IDX(hwif); + rss_key.template_id = template_id; + memcpy(rss_key.key, temp, HINIC_RSS_KEY_SIZE); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RSS_TEMPLATE_TBL, + &rss_key, sizeof(rss_key), + &rss_key, &out_size); + if (err || !out_size || rss_key.status) { + dev_err(&pdev->dev, + "Failed to set rss hash key, err: %d, status: 0x%x, out size: 0x%x\n", + err, rss_key.status, out_size); + return -EINVAL; + } + + return 0; +} + +int hinic_rss_get_template_tbl(struct hinic_dev *nic_dev, u32 tmpl_idx, + u8 *temp) +{ + struct hinic_rss_template_key temp_key = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif; + struct pci_dev *pdev; + u16 out_size = sizeof(temp_key); + int err; + + if (!hwdev || !temp) + return -EINVAL; + + hwif = hwdev->hwif; + pdev = hwif->pdev; + + temp_key.func_id = HINIC_HWIF_FUNC_IDX(hwif); + temp_key.template_id = tmpl_idx; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_RSS_TEMPLATE_TBL, + &temp_key, sizeof(temp_key), + &temp_key, &out_size); + if (err || !out_size || temp_key.status) { + dev_err(&pdev->dev, "Failed to set hash key, err: %d, status: 0x%x, out size: 0x%x\n", + err, temp_key.status, out_size); + return -EINVAL; + } + + memcpy(temp, temp_key.key, HINIC_RSS_KEY_SIZE); + + return 0; +} + +int hinic_rss_set_hash_engine(struct hinic_dev *nic_dev, u8 template_id, + u8 type) +{ + struct hinic_rss_engine_type rss_engine = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + rss_engine.func_id = HINIC_HWIF_FUNC_IDX(hwif); + rss_engine.hash_engine = type; + rss_engine.template_id = template_id; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RSS_HASH_ENGINE, + &rss_engine, sizeof(rss_engine), + &rss_engine, &out_size); + if (err || !out_size || rss_engine.status) { + dev_err(&pdev->dev, + "Failed to set hash engine, err: %d, status: 0x%x, out size: 0x%x\n", + err, rss_engine.status, out_size); + return -EINVAL; + } + + return 0; +} + +int hinic_rss_get_hash_engine(struct hinic_dev *nic_dev, u8 tmpl_idx, u8 *type) +{ + struct hinic_rss_engine_type hash_type = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif; + struct pci_dev *pdev; + u16 out_size = sizeof(hash_type); + int err; + + if (!hwdev || !type) + return -EINVAL; + + hwif = hwdev->hwif; + pdev = hwif->pdev; + + hash_type.func_id = HINIC_HWIF_FUNC_IDX(hwif); + hash_type.template_id = tmpl_idx; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_RSS_HASH_ENGINE, + &hash_type, sizeof(hash_type), + &hash_type, &out_size); + if (err || !out_size || hash_type.status) { + dev_err(&pdev->dev, "Failed to get hash engine, err: %d, status: 0x%x, out size: 0x%x\n", + err, hash_type.status, out_size); + return -EINVAL; + } + + *type = hash_type.hash_engine; + return 0; +} + +int hinic_rss_cfg(struct hinic_dev *nic_dev, u8 rss_en, u8 template_id) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_rss_config rss_cfg = { 0 }; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + rss_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif); + rss_cfg.rss_en = rss_en; + rss_cfg.template_id = template_id; + rss_cfg.rq_priority_number = 0; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_RSS_CFG, + &rss_cfg, sizeof(rss_cfg), + &rss_cfg, &out_size); + if (err || !out_size || rss_cfg.status) { + dev_err(&pdev->dev, + "Failed to set rss cfg, err: %d, status: 0x%x, out size: 0x%x\n", + err, rss_cfg.status, out_size); + return -EINVAL; + } + + return 0; +} + +int hinic_rss_template_alloc(struct hinic_dev *nic_dev, u8 *tmpl_idx) +{ + struct hinic_rss_template_mgmt template_mgmt = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + template_mgmt.func_id = HINIC_HWIF_FUNC_IDX(hwif); + template_mgmt.cmd = NIC_RSS_CMD_TEMP_ALLOC; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_RSS_TEMP_MGR, + &template_mgmt, sizeof(template_mgmt), + &template_mgmt, &out_size); + if (err || !out_size || template_mgmt.status) { + dev_err(&pdev->dev, "Failed to alloc rss template, err: %d, status: 0x%x, out size: 0x%x\n", + err, template_mgmt.status, out_size); + return -EINVAL; + } + + *tmpl_idx = template_mgmt.template_id; + + return 0; +} + +int hinic_rss_template_free(struct hinic_dev *nic_dev, u8 tmpl_idx) +{ + struct hinic_rss_template_mgmt template_mgmt = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + template_mgmt.func_id = HINIC_HWIF_FUNC_IDX(hwif); + template_mgmt.template_id = tmpl_idx; + template_mgmt.cmd = NIC_RSS_CMD_TEMP_FREE; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_RSS_TEMP_MGR, + &template_mgmt, sizeof(template_mgmt), + &template_mgmt, &out_size); + if (err || !out_size || template_mgmt.status) { + dev_err(&pdev->dev, "Failed to free rss template, err: %d, status: 0x%x, out size: 0x%x\n", + err, template_mgmt.status, out_size); + return -EINVAL; + } + + return 0; +} + +int hinic_get_vport_stats(struct hinic_dev *nic_dev, + struct hinic_vport_stats *stats) +{ + struct hinic_cmd_vport_stats vport_stats = { 0 }; + struct hinic_port_stats_info stats_info = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + u16 out_size = sizeof(vport_stats); + struct pci_dev *pdev = hwif->pdev; + int err; + + stats_info.stats_version = HINIC_PORT_STATS_VERSION; + stats_info.func_id = HINIC_HWIF_FUNC_IDX(hwif); + stats_info.stats_size = sizeof(vport_stats); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_VPORT_STAT, + &stats_info, sizeof(stats_info), + &vport_stats, &out_size); + if (err || !out_size || vport_stats.status) { + dev_err(&pdev->dev, + "Failed to get function statistics, err: %d, status: 0x%x, out size: 0x%x\n", + err, vport_stats.status, out_size); + return -EFAULT; + } + + memcpy(stats, &vport_stats.stats, sizeof(*stats)); + return 0; +} + +int hinic_get_phy_port_stats(struct hinic_dev *nic_dev, + struct hinic_phy_port_stats *stats) +{ + struct hinic_port_stats_info stats_info = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct hinic_port_stats *port_stats; + u16 out_size = sizeof(*port_stats); + struct pci_dev *pdev = hwif->pdev; + int err; + + port_stats = kzalloc(sizeof(*port_stats), GFP_KERNEL); + if (!port_stats) + return -ENOMEM; + + stats_info.stats_version = HINIC_PORT_STATS_VERSION; + stats_info.stats_size = sizeof(*port_stats); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_PORT_STATISTICS, + &stats_info, sizeof(stats_info), + port_stats, &out_size); + if (err || !out_size || port_stats->status) { + dev_err(&pdev->dev, + "Failed to get port statistics, err: %d, status: 0x%x, out size: 0x%x\n", + err, port_stats->status, out_size); + err = -EINVAL; + goto out; + } + + memcpy(stats, &port_stats->stats, sizeof(*stats)); + +out: + kfree(port_stats); + + return err; +} |