xref: /linux/drivers/dma/ptdma/ptdma-pci.c (revision 69bfec7548f4c1595bac0e3ddfc0458a5af31f4c)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * AMD Passthru DMA device driver
4  * -- Based on the CCP driver
5  *
6  * Copyright (C) 2016,2021 Advanced Micro Devices, Inc.
7  *
8  * Author: Sanjay R Mehta <sanju.mehta@amd.com>
9  * Author: Tom Lendacky <thomas.lendacky@amd.com>
10  * Author: Gary R Hook <gary.hook@amd.com>
11  */
12 
13 #include <linux/device.h>
14 #include <linux/dma-mapping.h>
15 #include <linux/delay.h>
16 #include <linux/interrupt.h>
17 #include <linux/kernel.h>
18 #include <linux/kthread.h>
19 #include <linux/module.h>
20 #include <linux/pci_ids.h>
21 #include <linux/pci.h>
22 #include <linux/spinlock.h>
23 
24 #include "ptdma.h"
25 
26 struct pt_msix {
27 	int msix_count;
28 	struct msix_entry msix_entry;
29 };
30 
31 /*
32  * pt_alloc_struct - allocate and initialize the pt_device struct
33  *
34  * @dev: device struct of the PTDMA
35  */
36 static struct pt_device *pt_alloc_struct(struct device *dev)
37 {
38 	struct pt_device *pt;
39 
40 	pt = devm_kzalloc(dev, sizeof(*pt), GFP_KERNEL);
41 
42 	if (!pt)
43 		return NULL;
44 	pt->dev = dev;
45 
46 	INIT_LIST_HEAD(&pt->cmd);
47 
48 	return pt;
49 }
50 
51 static int pt_get_msix_irqs(struct pt_device *pt)
52 {
53 	struct pt_msix *pt_msix = pt->pt_msix;
54 	struct device *dev = pt->dev;
55 	struct pci_dev *pdev = to_pci_dev(dev);
56 	int ret;
57 
58 	pt_msix->msix_entry.entry = 0;
59 
60 	ret = pci_enable_msix_range(pdev, &pt_msix->msix_entry, 1, 1);
61 	if (ret < 0)
62 		return ret;
63 
64 	pt_msix->msix_count = ret;
65 
66 	pt->pt_irq = pt_msix->msix_entry.vector;
67 
68 	return 0;
69 }
70 
71 static int pt_get_msi_irq(struct pt_device *pt)
72 {
73 	struct device *dev = pt->dev;
74 	struct pci_dev *pdev = to_pci_dev(dev);
75 	int ret;
76 
77 	ret = pci_enable_msi(pdev);
78 	if (ret)
79 		return ret;
80 
81 	pt->pt_irq = pdev->irq;
82 
83 	return 0;
84 }
85 
86 static int pt_get_irqs(struct pt_device *pt)
87 {
88 	struct device *dev = pt->dev;
89 	int ret;
90 
91 	ret = pt_get_msix_irqs(pt);
92 	if (!ret)
93 		return 0;
94 
95 	/* Couldn't get MSI-X vectors, try MSI */
96 	dev_err(dev, "could not enable MSI-X (%d), trying MSI\n", ret);
97 	ret = pt_get_msi_irq(pt);
98 	if (!ret)
99 		return 0;
100 
101 	/* Couldn't get MSI interrupt */
102 	dev_err(dev, "could not enable MSI (%d)\n", ret);
103 
104 	return ret;
105 }
106 
107 static void pt_free_irqs(struct pt_device *pt)
108 {
109 	struct pt_msix *pt_msix = pt->pt_msix;
110 	struct device *dev = pt->dev;
111 	struct pci_dev *pdev = to_pci_dev(dev);
112 
113 	if (pt_msix->msix_count)
114 		pci_disable_msix(pdev);
115 	else if (pt->pt_irq)
116 		pci_disable_msi(pdev);
117 
118 	pt->pt_irq = 0;
119 }
120 
121 static int pt_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
122 {
123 	struct pt_device *pt;
124 	struct pt_msix *pt_msix;
125 	struct device *dev = &pdev->dev;
126 	void __iomem * const *iomap_table;
127 	int bar_mask;
128 	int ret = -ENOMEM;
129 
130 	pt = pt_alloc_struct(dev);
131 	if (!pt)
132 		goto e_err;
133 
134 	pt_msix = devm_kzalloc(dev, sizeof(*pt_msix), GFP_KERNEL);
135 	if (!pt_msix)
136 		goto e_err;
137 
138 	pt->pt_msix = pt_msix;
139 	pt->dev_vdata = (struct pt_dev_vdata *)id->driver_data;
140 	if (!pt->dev_vdata) {
141 		ret = -ENODEV;
142 		dev_err(dev, "missing driver data\n");
143 		goto e_err;
144 	}
145 
146 	ret = pcim_enable_device(pdev);
147 	if (ret) {
148 		dev_err(dev, "pcim_enable_device failed (%d)\n", ret);
149 		goto e_err;
150 	}
151 
152 	bar_mask = pci_select_bars(pdev, IORESOURCE_MEM);
153 	ret = pcim_iomap_regions(pdev, bar_mask, "ptdma");
154 	if (ret) {
155 		dev_err(dev, "pcim_iomap_regions failed (%d)\n", ret);
156 		goto e_err;
157 	}
158 
159 	iomap_table = pcim_iomap_table(pdev);
160 	if (!iomap_table) {
161 		dev_err(dev, "pcim_iomap_table failed\n");
162 		ret = -ENOMEM;
163 		goto e_err;
164 	}
165 
166 	pt->io_regs = iomap_table[pt->dev_vdata->bar];
167 	if (!pt->io_regs) {
168 		dev_err(dev, "ioremap failed\n");
169 		ret = -ENOMEM;
170 		goto e_err;
171 	}
172 
173 	ret = pt_get_irqs(pt);
174 	if (ret)
175 		goto e_err;
176 
177 	pci_set_master(pdev);
178 
179 	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
180 	if (ret) {
181 		ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
182 		if (ret) {
183 			dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n",
184 				ret);
185 			goto e_err;
186 		}
187 	}
188 
189 	dev_set_drvdata(dev, pt);
190 
191 	if (pt->dev_vdata)
192 		ret = pt_core_init(pt);
193 
194 	if (ret)
195 		goto e_err;
196 
197 	return 0;
198 
199 e_err:
200 	dev_err(dev, "initialization failed ret = %d\n", ret);
201 
202 	return ret;
203 }
204 
205 static void pt_pci_remove(struct pci_dev *pdev)
206 {
207 	struct device *dev = &pdev->dev;
208 	struct pt_device *pt = dev_get_drvdata(dev);
209 
210 	if (!pt)
211 		return;
212 
213 	if (pt->dev_vdata)
214 		pt_core_destroy(pt);
215 
216 	pt_free_irqs(pt);
217 }
218 
219 static const struct pt_dev_vdata dev_vdata[] = {
220 	{
221 		.bar = 2,
222 	},
223 };
224 
225 static const struct pci_device_id pt_pci_table[] = {
226 	{ PCI_VDEVICE(AMD, 0x1498), (kernel_ulong_t)&dev_vdata[0] },
227 	/* Last entry must be zero */
228 	{ 0, }
229 };
230 MODULE_DEVICE_TABLE(pci, pt_pci_table);
231 
232 static struct pci_driver pt_pci_driver = {
233 	.name = "ptdma",
234 	.id_table = pt_pci_table,
235 	.probe = pt_pci_probe,
236 	.remove = pt_pci_remove,
237 };
238 
239 module_pci_driver(pt_pci_driver);
240 
241 MODULE_AUTHOR("Sanjay R Mehta <sanju.mehta@amd.com>");
242 MODULE_LICENSE("GPL");
243 MODULE_DESCRIPTION("AMD PassThru DMA driver");
244