Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * AMD Passthru DMA device driver
0004  * -- Based on the CCP driver
0005  *
0006  * Copyright (C) 2016,2021 Advanced Micro Devices, Inc.
0007  *
0008  * Author: Sanjay R Mehta <sanju.mehta@amd.com>
0009  * Author: Tom Lendacky <thomas.lendacky@amd.com>
0010  * Author: Gary R Hook <gary.hook@amd.com>
0011  */
0012 
0013 #include <linux/device.h>
0014 #include <linux/dma-mapping.h>
0015 #include <linux/delay.h>
0016 #include <linux/interrupt.h>
0017 #include <linux/kernel.h>
0018 #include <linux/kthread.h>
0019 #include <linux/module.h>
0020 #include <linux/pci_ids.h>
0021 #include <linux/pci.h>
0022 #include <linux/spinlock.h>
0023 
0024 #include "ptdma.h"
0025 
0026 struct pt_msix {
0027     int msix_count;
0028     struct msix_entry msix_entry;
0029 };
0030 
0031 /*
0032  * pt_alloc_struct - allocate and initialize the pt_device struct
0033  *
0034  * @dev: device struct of the PTDMA
0035  */
0036 static struct pt_device *pt_alloc_struct(struct device *dev)
0037 {
0038     struct pt_device *pt;
0039 
0040     pt = devm_kzalloc(dev, sizeof(*pt), GFP_KERNEL);
0041 
0042     if (!pt)
0043         return NULL;
0044     pt->dev = dev;
0045 
0046     INIT_LIST_HEAD(&pt->cmd);
0047 
0048     return pt;
0049 }
0050 
0051 static int pt_get_msix_irqs(struct pt_device *pt)
0052 {
0053     struct pt_msix *pt_msix = pt->pt_msix;
0054     struct device *dev = pt->dev;
0055     struct pci_dev *pdev = to_pci_dev(dev);
0056     int ret;
0057 
0058     pt_msix->msix_entry.entry = 0;
0059 
0060     ret = pci_enable_msix_range(pdev, &pt_msix->msix_entry, 1, 1);
0061     if (ret < 0)
0062         return ret;
0063 
0064     pt_msix->msix_count = ret;
0065 
0066     pt->pt_irq = pt_msix->msix_entry.vector;
0067 
0068     return 0;
0069 }
0070 
0071 static int pt_get_msi_irq(struct pt_device *pt)
0072 {
0073     struct device *dev = pt->dev;
0074     struct pci_dev *pdev = to_pci_dev(dev);
0075     int ret;
0076 
0077     ret = pci_enable_msi(pdev);
0078     if (ret)
0079         return ret;
0080 
0081     pt->pt_irq = pdev->irq;
0082 
0083     return 0;
0084 }
0085 
0086 static int pt_get_irqs(struct pt_device *pt)
0087 {
0088     struct device *dev = pt->dev;
0089     int ret;
0090 
0091     ret = pt_get_msix_irqs(pt);
0092     if (!ret)
0093         return 0;
0094 
0095     /* Couldn't get MSI-X vectors, try MSI */
0096     dev_err(dev, "could not enable MSI-X (%d), trying MSI\n", ret);
0097     ret = pt_get_msi_irq(pt);
0098     if (!ret)
0099         return 0;
0100 
0101     /* Couldn't get MSI interrupt */
0102     dev_err(dev, "could not enable MSI (%d)\n", ret);
0103 
0104     return ret;
0105 }
0106 
0107 static void pt_free_irqs(struct pt_device *pt)
0108 {
0109     struct pt_msix *pt_msix = pt->pt_msix;
0110     struct device *dev = pt->dev;
0111     struct pci_dev *pdev = to_pci_dev(dev);
0112 
0113     if (pt_msix->msix_count)
0114         pci_disable_msix(pdev);
0115     else if (pt->pt_irq)
0116         pci_disable_msi(pdev);
0117 
0118     pt->pt_irq = 0;
0119 }
0120 
0121 static int pt_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
0122 {
0123     struct pt_device *pt;
0124     struct pt_msix *pt_msix;
0125     struct device *dev = &pdev->dev;
0126     void __iomem * const *iomap_table;
0127     int bar_mask;
0128     int ret = -ENOMEM;
0129 
0130     pt = pt_alloc_struct(dev);
0131     if (!pt)
0132         goto e_err;
0133 
0134     pt_msix = devm_kzalloc(dev, sizeof(*pt_msix), GFP_KERNEL);
0135     if (!pt_msix)
0136         goto e_err;
0137 
0138     pt->pt_msix = pt_msix;
0139     pt->dev_vdata = (struct pt_dev_vdata *)id->driver_data;
0140     if (!pt->dev_vdata) {
0141         ret = -ENODEV;
0142         dev_err(dev, "missing driver data\n");
0143         goto e_err;
0144     }
0145 
0146     ret = pcim_enable_device(pdev);
0147     if (ret) {
0148         dev_err(dev, "pcim_enable_device failed (%d)\n", ret);
0149         goto e_err;
0150     }
0151 
0152     bar_mask = pci_select_bars(pdev, IORESOURCE_MEM);
0153     ret = pcim_iomap_regions(pdev, bar_mask, "ptdma");
0154     if (ret) {
0155         dev_err(dev, "pcim_iomap_regions failed (%d)\n", ret);
0156         goto e_err;
0157     }
0158 
0159     iomap_table = pcim_iomap_table(pdev);
0160     if (!iomap_table) {
0161         dev_err(dev, "pcim_iomap_table failed\n");
0162         ret = -ENOMEM;
0163         goto e_err;
0164     }
0165 
0166     pt->io_regs = iomap_table[pt->dev_vdata->bar];
0167     if (!pt->io_regs) {
0168         dev_err(dev, "ioremap failed\n");
0169         ret = -ENOMEM;
0170         goto e_err;
0171     }
0172 
0173     ret = pt_get_irqs(pt);
0174     if (ret)
0175         goto e_err;
0176 
0177     pci_set_master(pdev);
0178 
0179     ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
0180     if (ret) {
0181         ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
0182         if (ret) {
0183             dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n",
0184                 ret);
0185             goto e_err;
0186         }
0187     }
0188 
0189     dev_set_drvdata(dev, pt);
0190 
0191     if (pt->dev_vdata)
0192         ret = pt_core_init(pt);
0193 
0194     if (ret)
0195         goto e_err;
0196 
0197     return 0;
0198 
0199 e_err:
0200     dev_err(dev, "initialization failed ret = %d\n", ret);
0201 
0202     return ret;
0203 }
0204 
0205 static void pt_pci_remove(struct pci_dev *pdev)
0206 {
0207     struct device *dev = &pdev->dev;
0208     struct pt_device *pt = dev_get_drvdata(dev);
0209 
0210     if (!pt)
0211         return;
0212 
0213     if (pt->dev_vdata)
0214         pt_core_destroy(pt);
0215 
0216     pt_free_irqs(pt);
0217 }
0218 
0219 static const struct pt_dev_vdata dev_vdata[] = {
0220     {
0221         .bar = 2,
0222     },
0223 };
0224 
0225 static const struct pci_device_id pt_pci_table[] = {
0226     { PCI_VDEVICE(AMD, 0x1498), (kernel_ulong_t)&dev_vdata[0] },
0227     /* Last entry must be zero */
0228     { 0, }
0229 };
0230 MODULE_DEVICE_TABLE(pci, pt_pci_table);
0231 
0232 static struct pci_driver pt_pci_driver = {
0233     .name = "ptdma",
0234     .id_table = pt_pci_table,
0235     .probe = pt_pci_probe,
0236     .remove = pt_pci_remove,
0237 };
0238 
0239 module_pci_driver(pt_pci_driver);
0240 
0241 MODULE_AUTHOR("Sanjay R Mehta <sanju.mehta@amd.com>");
0242 MODULE_LICENSE("GPL");
0243 MODULE_DESCRIPTION("AMD PassThru DMA driver");