// SPDX-License-Identifier: GPL-2.0+ // Copyright (c) 2022 Hisilicon Limited. #include #include #include #include #include "core.h" #include "hnae3.h" #include "hns3_device.h" #include "hns3_common.h" #include "hns3_cmdq.h" #include "hns3_verbs.h" #include "hns3_intr.h" static struct workqueue_struct *hns3_roh_wq; static struct dentry *hns3_roh_dfx_root; static const struct pci_device_id hns3_roh_pci_tbl[] = { { PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_100G_ROH), 0 }, { PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_200G_ROH), 0 }, { PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_400G_ROH), 0 }, { 0, } }; MODULE_DEVICE_TABLE(pci, hns3_roh_pci_tbl); static int hns3_roh_get_intr_cap(struct hns3_roh_device *hroh_dev) { struct hns3_roh_get_intr_info *resp; struct hns3_roh_desc desc; int ret; hns3_roh_cmdq_setup_basic_desc(&desc, HNS3_ROH_OPC_GET_INTR_INFO, true); ret = hns3_roh_cmdq_send(hroh_dev, &desc, 1); if (ret) { dev_err(hroh_dev->dev, "failed to get intr info, ret = %d\n", ret); return ret; } resp = (struct hns3_roh_get_intr_info *)desc.data; hroh_dev->intr_info.vector_offset = le16_to_cpu(resp->msixcap_localid_number_nic) + le16_to_cpu(resp->pf_intr_vector_number_roce); hroh_dev->intr_info.vector_num = le16_to_cpu(resp->pf_intr_vector_number_roh); if (hroh_dev->intr_info.vector_num < HNS3_ROH_MIN_VECTOR_NUM) { dev_err(hroh_dev->dev, "just %d intr resources, not enough(min: %d).\n", hroh_dev->intr_info.vector_num, HNS3_ROH_MIN_VECTOR_NUM); return -EINVAL; } return 0; } static void hns3_roh_unregister_device(struct hns3_roh_device *hroh_dev) { hroh_dev->active = false; roh_unregister_device(&hroh_dev->roh_dev); } static int hns3_roh_register_device(struct hns3_roh_device *hroh_dev) { struct roh_device *rohdev = &hroh_dev->roh_dev; struct device *dev = hroh_dev->dev; int ret; if (!strlen(rohdev->name)) strscpy(rohdev->name, "hns3_%d", ROH_DEVICE_NAME_MAX); rohdev->owner = THIS_MODULE; rohdev->dev.parent = dev; rohdev->netdev = hroh_dev->netdev; rohdev->ops.set_eid = hns3_roh_set_eid; rohdev->ops.alloc_hw_stats = hns3_roh_alloc_hw_stats; rohdev->ops.get_hw_stats = hns3_roh_get_hw_stats; ret = hns3_roh_get_link_status(hroh_dev, &rohdev->link_status); if (ret) { dev_err(dev, "failed to get link status, ret = %d\n", ret); return ret; } ret = roh_register_device(rohdev); if (ret) { dev_err(dev, "failed to register roh device, ret = %d\n", ret); return ret; } hroh_dev->active = true; return 0; } void hns3_roh_mbx_task_schedule(struct hns3_roh_device *hroh_dev) { if (!test_and_set_bit(HNS3_ROH_SW_STATE_MBX_SERVICE_SCHED, &hroh_dev->state)) mod_delayed_work(hns3_roh_wq, &hroh_dev->srv_task, 0); } void hns3_roh_task_schedule(struct hns3_roh_device *hroh_dev, unsigned long delay_time) { mod_delayed_work(hns3_roh_wq, &hroh_dev->srv_task, delay_time); } static void hns3_roh_mbx_service_task(struct hns3_roh_device *hroh_dev) { if (!test_and_clear_bit(HNS3_ROH_SW_STATE_MBX_SERVICE_SCHED, &hroh_dev->state) || test_and_set_bit(HNS3_ROH_SW_STATE_MBX_HANDLING, &hroh_dev->state)) return; hns3_roh_mbx_handler(hroh_dev); clear_bit(HNS3_ROH_SW_STATE_MBX_HANDLING, &hroh_dev->state); } static void hns3_roh_poll_service_task(struct hns3_roh_device *hroh_dev) { unsigned long delta = round_jiffies_relative(HZ); hns3_roh_update_link_status(hroh_dev); if (time_is_after_jiffies(hroh_dev->last_processed + HZ)) { delta = jiffies - hroh_dev->last_processed; if (delta < round_jiffies_relative(HZ)) { delta = round_jiffies_relative(HZ) - delta; goto out; } } hroh_dev->last_processed = jiffies; out: hns3_roh_task_schedule(hroh_dev, delta); } static void hns3_roh_service_task(struct work_struct *work) { struct hns3_roh_device *hroh_dev = container_of(work, struct hns3_roh_device, srv_task.work); hns3_roh_mbx_service_task(hroh_dev); hns3_roh_poll_service_task(hroh_dev); } static void hns3_roh_dev_sw_state_init(struct hns3_roh_device *hroh_dev) { clear_bit(HNS3_ROH_SW_STATE_MBX_SERVICE_SCHED, &hroh_dev->state); clear_bit(HNS3_ROH_SW_STATE_MBX_HANDLING, &hroh_dev->state); } static int hns3_roh_init_hw(struct hns3_roh_device *hroh_dev) { struct device *dev = hroh_dev->dev; int ret; ret = hroh_dev->hw->cmdq_init(hroh_dev); if (ret) { dev_err(dev, "failed to init cmdq, ret = %d\n", ret); return ret; } ret = hroh_dev->hw->get_intr_cap(hroh_dev); if (ret) { dev_err(dev, "failed to get intr cap, ret = %d\n", ret); goto err_free_cmdq; } ret = hns3_roh_init_irq(hroh_dev); if (ret) { dev_err(dev, "failed to init irq, ret = %d\n", ret); goto err_free_cmdq; } return 0; err_free_cmdq: hroh_dev->hw->cmdq_exit(hroh_dev); return ret; } static void hns3_roh_uninit_hw(struct hns3_roh_device *hroh_dev) { hns3_roh_uninit_irq(hroh_dev); hroh_dev->hw->cmdq_exit(hroh_dev); } static int hns3_roh_init(struct hns3_roh_device *hroh_dev) { struct device *dev = hroh_dev->dev; int ret; ret = hns3_roh_init_hw(hroh_dev); if (ret) { dev_err(dev, "failed to init hw resources, ret = %d\n", ret); return ret; } ret = hns3_roh_register_device(hroh_dev); if (ret) { dev_err(dev, "failed to register roh device, ret = %d\n", ret); goto err_uninit_hw; } INIT_DELAYED_WORK(&hroh_dev->srv_task, hns3_roh_service_task); hns3_roh_enable_vector(&hroh_dev->abn_vector, true); hns3_roh_dev_sw_state_init(hroh_dev); hns3_roh_task_schedule(hroh_dev, round_jiffies_relative(HZ)); dev_info(dev, "%s driver init success.\n", HNS3_ROH_NAME); return 0; err_uninit_hw: hns3_roh_uninit_hw(hroh_dev); return ret; } static void hns3_roh_exit(struct hns3_roh_device *hroh_dev) { cancel_delayed_work_sync(&hroh_dev->srv_task); hns3_roh_unregister_device(hroh_dev); hns3_roh_uninit_hw(hroh_dev); dev_info(&hroh_dev->pdev->dev, "%s driver uninit success.\n", HNS3_ROH_NAME); } static const struct hns3_roh_hw hns3_roh_hw = { .cmdq_init = hns3_roh_cmdq_init, .cmdq_exit = hns3_roh_cmdq_exit, .get_intr_cap = hns3_roh_get_intr_cap, }; static void hns3_roh_get_cfg_from_frame(struct hns3_roh_device *hroh_dev, struct hnae3_handle *handle) { hroh_dev->pdev = handle->pdev; hroh_dev->dev = &handle->pdev->dev; hroh_dev->netdev = handle->rohinfo.netdev; hroh_dev->reg_base = handle->rohinfo.roh_io_base; hroh_dev->hw = &hns3_roh_hw; hroh_dev->priv->handle = handle; } static void hns3_roh_dfx_init(struct hns3_roh_device *hroh_dev); static int __hns3_roh_init_instance(struct hnae3_handle *handle) { struct hns3_roh_device *hroh_dev; int ret; hroh_dev = (struct hns3_roh_device *)roh_alloc_device(sizeof(*hroh_dev)); if (!hroh_dev) { dev_err(&handle->pdev->dev, "failed to alloc roh dev.\n"); return -ENOMEM; } hroh_dev->priv = kzalloc(sizeof(*hroh_dev->priv), GFP_KERNEL); if (!hroh_dev->priv) { ret = -ENOMEM; goto err_roh_alloc_device; } hns3_roh_get_cfg_from_frame(hroh_dev, handle); ret = hns3_roh_init(hroh_dev); if (ret) { dev_err(hroh_dev->dev, "failed to init roh, ret = %d\n", ret); goto err_kzalloc; } handle->priv = hroh_dev; set_bit(HNS3_ROH_STATE_INITED, &handle->rohinfo.reset_state); hns3_roh_dfx_init(hroh_dev); return 0; err_kzalloc: kfree(hroh_dev->priv); err_roh_alloc_device: roh_dealloc_device(&hroh_dev->roh_dev); return ret; } static void hns3_roh_dfx_uninit(struct hns3_roh_device *hroh_dev); static void __hns3_roh_uninit_instance(struct hnae3_handle *handle) { struct hns3_roh_device *hroh_dev = (struct hns3_roh_device *)handle->priv; if (!hroh_dev) return; hns3_roh_dfx_uninit(hroh_dev); if (!test_and_clear_bit(HNS3_ROH_STATE_INITED, &handle->rohinfo.reset_state)) netdev_warn(hroh_dev->netdev, "already uninitialized\n"); hns3_roh_enable_vector(&hroh_dev->abn_vector, false); handle->priv = NULL; hns3_roh_exit(hroh_dev); kfree(hroh_dev->priv); roh_dealloc_device(&hroh_dev->roh_dev); } static int hns3_roh_init_instance(struct hnae3_handle *handle) { struct device *dev = &handle->pdev->dev; const struct pci_device_id *id; int ret; id = pci_match_id(hns3_roh_pci_tbl, handle->pdev); if (!id) return 0; if (id->driver_data) { dev_err(dev, "not support vf.\n"); return -EINVAL; } ret = __hns3_roh_init_instance(handle); if (ret) { dev_err(dev, "failed to init instance, ret = %d\n", ret); return ret; } return 0; } static void hns3_roh_uninit_instance(struct hnae3_handle *handle, bool reset) { __hns3_roh_uninit_instance(handle); } static int hns3_roh_reset_notify_init(struct hnae3_handle *handle) { struct device *dev = &handle->pdev->dev; int ret; ret = __hns3_roh_init_instance(handle); if (ret) { dev_err(dev, "failed to reinit in roh reset process, ret = %d\n", ret); handle->priv = NULL; clear_bit(HNS3_ROH_STATE_INITED, &handle->rohinfo.reset_state); } return 0; } static int hns3_roh_reset_notify_uninit(struct hnae3_handle *handle) { msleep(HNS3_ROH_HW_RST_UNINT_DELAY); __hns3_roh_uninit_instance(handle); return 0; } static int hns3_roh_reset_notify(struct hnae3_handle *handle, enum hnae3_reset_notify_type type) { int ret = 0; switch (type) { case HNAE3_INIT_CLIENT: ret = hns3_roh_reset_notify_init(handle); break; case HNAE3_UNINIT_CLIENT: ret = hns3_roh_reset_notify_uninit(handle); break; case HNAE3_DOWN_CLIENT: set_bit(HNS3_ROH_STATE_CMD_DISABLE, &handle->rohinfo.reset_state); break; default: break; } return ret; } static const struct hnae3_client_ops hns3_roh_ops = { .init_instance = hns3_roh_init_instance, .uninit_instance = hns3_roh_uninit_instance, .reset_notify = hns3_roh_reset_notify, }; static struct hnae3_client hns3_roh_client = { .name = "hns3_roh_hw", .type = HNAE3_CLIENT_ROH, .ops = &hns3_roh_ops, }; static ssize_t hns3_roh_dfx_cmd_read(struct file *filp, char __user *buffer, size_t count, loff_t *pos) { #define HNS3_ROH_DFX_READ_LEN 256 int uncopy_bytes; char *buf; int len; if (*pos != 0) return 0; if (count < HNS3_ROH_DFX_READ_LEN) return -ENOSPC; buf = kzalloc(HNS3_ROH_DFX_READ_LEN, GFP_KERNEL); if (!buf) return -ENOMEM; len = scnprintf(buf, HNS3_ROH_DFX_READ_LEN, "%s\n", "echo help to cmd to get help info"); uncopy_bytes = copy_to_user(buffer, buf, len); kfree(buf); if (uncopy_bytes) return -EFAULT; return (*pos = len); } static void hns3_roh_dfx_help(struct hns3_roh_device *hroh_dev) { dev_info(hroh_dev->dev, "dev info\n"); } static void hns3_roh_dfx_get_vector_cap(struct hns3_roh_device *hroh_dev) { u16 roce_vector_num, nic_vector_num, roh_vector_num; struct hns3_roh_get_intr_info *resp; struct device *dev = hroh_dev->dev; struct hns3_roh_desc desc; int ret; hns3_roh_cmdq_setup_basic_desc(&desc, HNS3_ROH_OPC_GET_INTR_INFO, true); ret = hns3_roh_cmdq_send(hroh_dev, &desc, 1); if (ret) dev_warn(hroh_dev->dev, "failed to get intr info, ret = %d\n", ret); resp = (struct hns3_roh_get_intr_info *)desc.data; nic_vector_num = le16_to_cpu(resp->msixcap_localid_number_nic); roce_vector_num = le16_to_cpu(resp->pf_intr_vector_number_roce); roh_vector_num = le16_to_cpu(resp->pf_intr_vector_number_roh); dev_info(dev, "NIC vector num: %d\n", nic_vector_num); dev_info(dev, "RoCE vector num: %d\n", roce_vector_num); dev_info(dev, "ROH vector num: %d\n", roh_vector_num); dev_info(dev, "ROH vector offset: %d\n", hroh_dev->intr_info.vector_offset); } static void hns3_roh_dfx_dump_dev_info(struct hns3_roh_device *hroh_dev) { struct device *dev = hroh_dev->dev; dev_info(dev, "PCIe device id: 0x%x\n", hroh_dev->pdev->device); dev_info(dev, "PCIe device name: %s\n", pci_name(hroh_dev->pdev)); dev_info(dev, "Network device name: %s\n", netdev_name(hroh_dev->netdev)); dev_info(dev, "BAR2~3 base addr: 0x%llx\n", (u64)hroh_dev->reg_base); dev_info(dev, "Base vector: %d\n", hroh_dev->intr_info.base_vecotr); hns3_roh_dfx_get_vector_cap(hroh_dev); dev_info(dev, "ABN vector0 irq: %d\n", hroh_dev->abn_vector.vector_irq); dev_info(dev, "ABN vector0 addr: 0x%llx\n", (u64)hroh_dev->abn_vector.addr); dev_info(dev, "ABN vector0 name: %s\n", hroh_dev->abn_vector.name); } static int hns3_roh_dfx_check_cmd(struct hns3_roh_device *hroh_dev, char *cmd_buf) { int ret = 0; if (strncmp(cmd_buf, "help", strlen("help")) == 0) hns3_roh_dfx_help(hroh_dev); else if (strncmp(cmd_buf, "dev info", strlen("dev info")) == 0) hns3_roh_dfx_dump_dev_info(hroh_dev); else ret = -EOPNOTSUPP; return ret; } static ssize_t hns3_roh_dfx_cmd_write(struct file *filp, const char __user *buffer, size_t count, loff_t *pos) { #define HNS3_ROH_DFX_WRITE_LEN 1024 struct hns3_roh_device *hroh_dev = filp->private_data; char *cmd_buf, *cmd_buf_tmp; int uncopied_bytes; int ret; if (*pos != 0) return 0; if (count > HNS3_ROH_DFX_WRITE_LEN) return -ENOSPC; cmd_buf = kzalloc(count + 1, GFP_KERNEL); if (!cmd_buf) return count; uncopied_bytes = copy_from_user(cmd_buf, buffer, count); if (uncopied_bytes) { kfree(cmd_buf); return -EFAULT; } cmd_buf[count] = '\0'; cmd_buf_tmp = strchr(cmd_buf, '\n'); if (cmd_buf_tmp) { *cmd_buf_tmp = '\0'; count = cmd_buf_tmp - cmd_buf + 1; } ret = hns3_roh_dfx_check_cmd(hroh_dev, cmd_buf); if (ret) hns3_roh_dfx_help(hroh_dev); kfree(cmd_buf); cmd_buf = NULL; return count; } static const struct file_operations hns3_roh_dfx_cmd_fops = { .owner = THIS_MODULE, .open = simple_open, .read = hns3_roh_dfx_cmd_read, .write = hns3_roh_dfx_cmd_write, }; static void hns3_roh_dfx_init(struct hns3_roh_device *hroh_dev) { const char *name = pci_name(hroh_dev->pdev); struct dentry *entry; if (IS_ERR_OR_NULL(hns3_roh_dfx_root)) return; hroh_dev->dfx_debugfs = debugfs_create_dir(name, hns3_roh_dfx_root); if (IS_ERR_OR_NULL(hroh_dev->dfx_debugfs)) return; entry = debugfs_create_file("hns3_roh_dfx", 0600, hroh_dev->dfx_debugfs, hroh_dev, &hns3_roh_dfx_cmd_fops); if (IS_ERR_OR_NULL(entry)) { debugfs_remove_recursive(hroh_dev->dfx_debugfs); hroh_dev->dfx_debugfs = NULL; return; } } static void hns3_roh_dfx_uninit(struct hns3_roh_device *hroh_dev) { if (IS_ERR_OR_NULL(hroh_dev->dfx_debugfs)) return; debugfs_remove_recursive(hroh_dev->dfx_debugfs); hroh_dev->dfx_debugfs = NULL; } static void hns3_roh_dfx_register_debugfs(const char *dir_name) { hns3_roh_dfx_root = debugfs_create_dir(dir_name, NULL); if (IS_ERR_OR_NULL(hns3_roh_dfx_root)) return; } static void hns3_roh_dfx_unregister_debugfs(void) { if (IS_ERR_OR_NULL(hns3_roh_dfx_root)) return; debugfs_remove_recursive(hns3_roh_dfx_root); hns3_roh_dfx_root = NULL; } static int __init hns3_roh_module_init(void) { int ret; hns3_roh_wq = alloc_workqueue("%s", 0, 0, HNS3_ROH_NAME); if (!hns3_roh_wq) { pr_err("%s: failed to create wq.\n", HNS3_ROH_NAME); return -ENOMEM; } hns3_roh_dfx_register_debugfs(HNS3_ROH_NAME); ret = hnae3_register_client(&hns3_roh_client); if (ret) goto out; return 0; out: hns3_roh_dfx_unregister_debugfs(); destroy_workqueue(hns3_roh_wq); return ret; } static void __exit hns3_roh_module_cleanup(void) { hnae3_unregister_client(&hns3_roh_client); hns3_roh_dfx_unregister_debugfs(); destroy_workqueue(hns3_roh_wq); } module_init(hns3_roh_module_init); module_exit(hns3_roh_module_cleanup); MODULE_LICENSE("GPL"); MODULE_VERSION(HNS3_ROH_VERSION); MODULE_AUTHOR("Huawei Tech. Co., Ltd."); MODULE_DESCRIPTION("Hisilicon Hip09 Family ROH Driver");