| // SPDX-License-Identifier: GPL-2.0-only |
| /* Copyright (C) 2023 Intel Corporation */ |
| |
| #include "idpf.h" |
| #include "idpf_devids.h" |
| #include "idpf_virtchnl.h" |
| |
| #define DRV_SUMMARY "Intel(R) Infrastructure Data Path Function Linux Driver" |
| |
| MODULE_DESCRIPTION(DRV_SUMMARY); |
| MODULE_LICENSE("GPL"); |
| |
| /** |
| * idpf_remove - Device removal routine |
| * @pdev: PCI device information struct |
| */ |
| static void idpf_remove(struct pci_dev *pdev) |
| { |
| struct idpf_adapter *adapter = pci_get_drvdata(pdev); |
| int i; |
| |
| set_bit(IDPF_REMOVE_IN_PROG, adapter->flags); |
| |
| /* Wait until vc_event_task is done to consider if any hard reset is |
| * in progress else we may go ahead and release the resources but the |
| * thread doing the hard reset might continue the init path and |
| * end up in bad state. |
| */ |
| cancel_delayed_work_sync(&adapter->vc_event_task); |
| if (adapter->num_vfs) |
| idpf_sriov_configure(pdev, 0); |
| |
| idpf_vc_core_deinit(adapter); |
| |
| /* Be a good citizen and leave the device clean on exit */ |
| adapter->dev_ops.reg_ops.trigger_reset(adapter, IDPF_HR_FUNC_RESET); |
| idpf_deinit_dflt_mbx(adapter); |
| |
| if (!adapter->netdevs) |
| goto destroy_wqs; |
| |
| /* There are some cases where it's possible to still have netdevs |
| * registered with the stack at this point, e.g. if the driver detected |
| * a HW reset and rmmod is called before it fully recovers. Unregister |
| * any stale netdevs here. |
| */ |
| for (i = 0; i < adapter->max_vports; i++) { |
| if (!adapter->netdevs[i]) |
| continue; |
| if (adapter->netdevs[i]->reg_state != NETREG_UNINITIALIZED) |
| unregister_netdev(adapter->netdevs[i]); |
| free_netdev(adapter->netdevs[i]); |
| adapter->netdevs[i] = NULL; |
| } |
| |
| destroy_wqs: |
| destroy_workqueue(adapter->init_wq); |
| destroy_workqueue(adapter->serv_wq); |
| destroy_workqueue(adapter->mbx_wq); |
| destroy_workqueue(adapter->stats_wq); |
| destroy_workqueue(adapter->vc_event_wq); |
| |
| for (i = 0; i < adapter->max_vports; i++) { |
| kfree(adapter->vport_config[i]); |
| adapter->vport_config[i] = NULL; |
| } |
| kfree(adapter->vport_config); |
| adapter->vport_config = NULL; |
| kfree(adapter->netdevs); |
| adapter->netdevs = NULL; |
| kfree(adapter->vcxn_mngr); |
| adapter->vcxn_mngr = NULL; |
| |
| mutex_destroy(&adapter->vport_ctrl_lock); |
| mutex_destroy(&adapter->vector_lock); |
| mutex_destroy(&adapter->queue_lock); |
| mutex_destroy(&adapter->vc_buf_lock); |
| |
| pci_set_drvdata(pdev, NULL); |
| kfree(adapter); |
| } |
| |
| /** |
| * idpf_shutdown - PCI callback for shutting down device |
| * @pdev: PCI device information struct |
| */ |
| static void idpf_shutdown(struct pci_dev *pdev) |
| { |
| idpf_remove(pdev); |
| |
| if (system_state == SYSTEM_POWER_OFF) |
| pci_set_power_state(pdev, PCI_D3hot); |
| } |
| |
| /** |
| * idpf_cfg_hw - Initialize HW struct |
| * @adapter: adapter to setup hw struct for |
| * |
| * Returns 0 on success, negative on failure |
| */ |
| static int idpf_cfg_hw(struct idpf_adapter *adapter) |
| { |
| struct pci_dev *pdev = adapter->pdev; |
| struct idpf_hw *hw = &adapter->hw; |
| |
| hw->hw_addr = pcim_iomap_table(pdev)[0]; |
| if (!hw->hw_addr) { |
| pci_err(pdev, "failed to allocate PCI iomap table\n"); |
| |
| return -ENOMEM; |
| } |
| |
| hw->back = adapter; |
| |
| return 0; |
| } |
| |
| /** |
| * idpf_probe - Device initialization routine |
| * @pdev: PCI device information struct |
| * @ent: entry in idpf_pci_tbl |
| * |
| * Returns 0 on success, negative on failure |
| */ |
| static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
| { |
| struct device *dev = &pdev->dev; |
| struct idpf_adapter *adapter; |
| int err; |
| |
| adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); |
| if (!adapter) |
| return -ENOMEM; |
| |
| adapter->req_tx_splitq = true; |
| adapter->req_rx_splitq = true; |
| |
| switch (ent->device) { |
| case IDPF_DEV_ID_PF: |
| idpf_dev_ops_init(adapter); |
| break; |
| case IDPF_DEV_ID_VF: |
| idpf_vf_dev_ops_init(adapter); |
| adapter->crc_enable = true; |
| break; |
| default: |
| err = -ENODEV; |
| dev_err(&pdev->dev, "Unexpected dev ID 0x%x in idpf probe\n", |
| ent->device); |
| goto err_free; |
| } |
| |
| adapter->pdev = pdev; |
| err = pcim_enable_device(pdev); |
| if (err) |
| goto err_free; |
| |
| err = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); |
| if (err) { |
| pci_err(pdev, "pcim_iomap_regions failed %pe\n", ERR_PTR(err)); |
| |
| goto err_free; |
| } |
| |
| /* set up for high or low dma */ |
| err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); |
| if (err) { |
| pci_err(pdev, "DMA configuration failed: %pe\n", ERR_PTR(err)); |
| |
| goto err_free; |
| } |
| |
| pci_set_master(pdev); |
| pci_set_drvdata(pdev, adapter); |
| |
| adapter->init_wq = alloc_workqueue("%s-%s-init", 0, 0, |
| dev_driver_string(dev), |
| dev_name(dev)); |
| if (!adapter->init_wq) { |
| dev_err(dev, "Failed to allocate init workqueue\n"); |
| err = -ENOMEM; |
| goto err_free; |
| } |
| |
| adapter->serv_wq = alloc_workqueue("%s-%s-service", 0, 0, |
| dev_driver_string(dev), |
| dev_name(dev)); |
| if (!adapter->serv_wq) { |
| dev_err(dev, "Failed to allocate service workqueue\n"); |
| err = -ENOMEM; |
| goto err_serv_wq_alloc; |
| } |
| |
| adapter->mbx_wq = alloc_workqueue("%s-%s-mbx", 0, 0, |
| dev_driver_string(dev), |
| dev_name(dev)); |
| if (!adapter->mbx_wq) { |
| dev_err(dev, "Failed to allocate mailbox workqueue\n"); |
| err = -ENOMEM; |
| goto err_mbx_wq_alloc; |
| } |
| |
| adapter->stats_wq = alloc_workqueue("%s-%s-stats", 0, 0, |
| dev_driver_string(dev), |
| dev_name(dev)); |
| if (!adapter->stats_wq) { |
| dev_err(dev, "Failed to allocate workqueue\n"); |
| err = -ENOMEM; |
| goto err_stats_wq_alloc; |
| } |
| |
| adapter->vc_event_wq = alloc_workqueue("%s-%s-vc_event", 0, 0, |
| dev_driver_string(dev), |
| dev_name(dev)); |
| if (!adapter->vc_event_wq) { |
| dev_err(dev, "Failed to allocate virtchnl event workqueue\n"); |
| err = -ENOMEM; |
| goto err_vc_event_wq_alloc; |
| } |
| |
| /* setup msglvl */ |
| adapter->msg_enable = netif_msg_init(-1, IDPF_AVAIL_NETIF_M); |
| |
| err = idpf_cfg_hw(adapter); |
| if (err) { |
| dev_err(dev, "Failed to configure HW structure for adapter: %d\n", |
| err); |
| goto err_cfg_hw; |
| } |
| |
| mutex_init(&adapter->vport_ctrl_lock); |
| mutex_init(&adapter->vector_lock); |
| mutex_init(&adapter->queue_lock); |
| mutex_init(&adapter->vc_buf_lock); |
| |
| INIT_DELAYED_WORK(&adapter->init_task, idpf_init_task); |
| INIT_DELAYED_WORK(&adapter->serv_task, idpf_service_task); |
| INIT_DELAYED_WORK(&adapter->mbx_task, idpf_mbx_task); |
| INIT_DELAYED_WORK(&adapter->stats_task, idpf_statistics_task); |
| INIT_DELAYED_WORK(&adapter->vc_event_task, idpf_vc_event_task); |
| |
| adapter->dev_ops.reg_ops.reset_reg_init(adapter); |
| set_bit(IDPF_HR_DRV_LOAD, adapter->flags); |
| queue_delayed_work(adapter->vc_event_wq, &adapter->vc_event_task, |
| msecs_to_jiffies(10 * (pdev->devfn & 0x07))); |
| |
| return 0; |
| |
| err_cfg_hw: |
| destroy_workqueue(adapter->vc_event_wq); |
| err_vc_event_wq_alloc: |
| destroy_workqueue(adapter->stats_wq); |
| err_stats_wq_alloc: |
| destroy_workqueue(adapter->mbx_wq); |
| err_mbx_wq_alloc: |
| destroy_workqueue(adapter->serv_wq); |
| err_serv_wq_alloc: |
| destroy_workqueue(adapter->init_wq); |
| err_free: |
| kfree(adapter); |
| return err; |
| } |
| |
| /* idpf_pci_tbl - PCI Dev idpf ID Table |
| */ |
| static const struct pci_device_id idpf_pci_tbl[] = { |
| { PCI_VDEVICE(INTEL, IDPF_DEV_ID_PF)}, |
| { PCI_VDEVICE(INTEL, IDPF_DEV_ID_VF)}, |
| { /* Sentinel */ } |
| }; |
| MODULE_DEVICE_TABLE(pci, idpf_pci_tbl); |
| |
| static struct pci_driver idpf_driver = { |
| .name = KBUILD_MODNAME, |
| .id_table = idpf_pci_tbl, |
| .probe = idpf_probe, |
| .sriov_configure = idpf_sriov_configure, |
| .remove = idpf_remove, |
| .shutdown = idpf_shutdown, |
| }; |
| module_pci_driver(idpf_driver); |