root/drivers/dma/amd/ae4dma/ae4dma-pci.c
// SPDX-License-Identifier: GPL-2.0
/*
 * AMD AE4DMA driver
 *
 * Copyright (c) 2024, Advanced Micro Devices, Inc.
 * All Rights Reserved.
 *
 * Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
 */

#include "ae4dma.h"

static int ae4_get_irqs(struct ae4_device *ae4)
{
        struct ae4_msix *ae4_msix = ae4->ae4_msix;
        struct pt_device *pt = &ae4->pt;
        struct device *dev = pt->dev;
        struct pci_dev *pdev;
        int i, v, ret;

        pdev = to_pci_dev(dev);

        for (v = 0; v < ARRAY_SIZE(ae4_msix->msix_entry); v++)
                ae4_msix->msix_entry[v].entry = v;

        ret = pci_alloc_irq_vectors(pdev, v, v, PCI_IRQ_MSIX);
        if (ret != v) {
                if (ret > 0)
                        pci_free_irq_vectors(pdev);

                dev_err(dev, "could not enable MSI-X (%d), trying MSI\n", ret);
                ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
                if (ret < 0) {
                        dev_err(dev, "could not enable MSI (%d)\n", ret);
                        return ret;
                }

                ret = pci_irq_vector(pdev, 0);
                if (ret < 0) {
                        pci_free_irq_vectors(pdev);
                        return ret;
                }

                for (i = 0; i < MAX_AE4_HW_QUEUES; i++)
                        ae4->ae4_irq[i] = ret;

        } else {
                ae4_msix->msix_count = ret;
                for (i = 0; i < ae4_msix->msix_count; i++)
                        ae4->ae4_irq[i] = pci_irq_vector(pdev, i);
        }

        return ret;
}

static void ae4_free_irqs(struct ae4_device *ae4)
{
        struct ae4_msix *ae4_msix = ae4->ae4_msix;
        struct pt_device *pt = &ae4->pt;
        struct device *dev = pt->dev;
        struct pci_dev *pdev;

        pdev = to_pci_dev(dev);

        if (ae4_msix && (ae4_msix->msix_count || ae4->ae4_irq[MAX_AE4_HW_QUEUES - 1]))
                pci_free_irq_vectors(pdev);
}

static void ae4_deinit(struct ae4_device *ae4)
{
        ae4_free_irqs(ae4);
}

static int ae4_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
        struct device *dev = &pdev->dev;
        struct ae4_device *ae4;
        struct pt_device *pt;
        int bar_mask;
        int ret = 0;

        ae4 = devm_kzalloc(dev, sizeof(*ae4), GFP_KERNEL);
        if (!ae4)
                return -ENOMEM;

        ae4->ae4_msix = devm_kzalloc(dev, sizeof(struct ae4_msix), GFP_KERNEL);
        if (!ae4->ae4_msix)
                return -ENOMEM;

        ret = pcim_enable_device(pdev);
        if (ret)
                goto ae4_error;

        bar_mask = pci_select_bars(pdev, IORESOURCE_MEM);
        ret = pcim_iomap_regions(pdev, bar_mask, "ae4dma");
        if (ret)
                goto ae4_error;

        pt = &ae4->pt;
        pt->dev = dev;
        pt->ver = AE4_DMA_VERSION;

        pt->io_regs = pcim_iomap_table(pdev)[0];
        if (!pt->io_regs) {
                ret = -ENOMEM;
                goto ae4_error;
        }

        ret = ae4_get_irqs(ae4);
        if (ret < 0)
                goto ae4_error;

        pci_set_master(pdev);

        dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));

        dev_set_drvdata(dev, ae4);

        ret = ae4_core_init(ae4);
        if (ret)
                goto ae4_error;

        return 0;

ae4_error:
        ae4_deinit(ae4);

        return ret;
}

static void ae4_pci_remove(struct pci_dev *pdev)
{
        struct ae4_device *ae4 = dev_get_drvdata(&pdev->dev);

        ae4_destroy_work(ae4);
        ae4_deinit(ae4);
}

static const struct pci_device_id ae4_pci_table[] = {
        { PCI_VDEVICE(AMD, 0x149B), },
        /* Last entry must be zero */
        { 0, }
};
MODULE_DEVICE_TABLE(pci, ae4_pci_table);

static struct pci_driver ae4_pci_driver = {
        .name = "ae4dma",
        .id_table = ae4_pci_table,
        .probe = ae4_pci_probe,
        .remove = ae4_pci_remove,
};

module_pci_driver(ae4_pci_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("AMD AE4DMA driver");