]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
63b94509 | 2 | /* |
d0ebbc0c | 3 | * AMD Secure Processor device driver |
63b94509 | 4 | * |
fa5cd1c7 | 5 | * Copyright (C) 2013,2018 Advanced Micro Devices, Inc. |
63b94509 TL |
6 | * |
7 | * Author: Tom Lendacky <thomas.lendacky@amd.com> | |
fba8855c | 8 | * Author: Gary R Hook <gary.hook@amd.com> |
63b94509 TL |
9 | */ |
10 | ||
11 | #include <linux/module.h> | |
12 | #include <linux/kernel.h> | |
3d77565b | 13 | #include <linux/device.h> |
63b94509 TL |
14 | #include <linux/pci.h> |
15 | #include <linux/pci_ids.h> | |
3d77565b | 16 | #include <linux/dma-mapping.h> |
63b94509 TL |
17 | #include <linux/kthread.h> |
18 | #include <linux/sched.h> | |
19 | #include <linux/interrupt.h> | |
20 | #include <linux/spinlock.h> | |
21 | #include <linux/delay.h> | |
22 | #include <linux/ccp.h> | |
23 | ||
24 | #include "ccp-dev.h" | |
2a6170df | 25 | #include "psp-dev.h" |
63b94509 | 26 | |
63b94509 TL |
27 | #define MSIX_VECTORS 2 |
28 | ||
d0ebbc0c | 29 | struct sp_pci { |
63b94509 | 30 | int msix_count; |
f4d18d65 | 31 | struct msix_entry msix_entry[MSIX_VECTORS]; |
63b94509 | 32 | }; |
2a6170df | 33 | static struct sp_device *sp_dev_master; |
63b94509 | 34 | |
d0ebbc0c | 35 | static int sp_get_msix_irqs(struct sp_device *sp) |
63b94509 | 36 | { |
d0ebbc0c | 37 | struct sp_pci *sp_pci = sp->dev_specific; |
f4d18d65 | 38 | struct device *dev = sp->dev; |
c6c59bf2 | 39 | struct pci_dev *pdev = to_pci_dev(dev); |
63b94509 TL |
40 | int v, ret; |
41 | ||
d0ebbc0c BS |
42 | for (v = 0; v < ARRAY_SIZE(sp_pci->msix_entry); v++) |
43 | sp_pci->msix_entry[v].entry = v; | |
63b94509 | 44 | |
d0ebbc0c | 45 | ret = pci_enable_msix_range(pdev, sp_pci->msix_entry, 1, v); |
5347ee8e | 46 | if (ret < 0) |
63b94509 TL |
47 | return ret; |
48 | ||
d0ebbc0c | 49 | sp_pci->msix_count = ret; |
f4d18d65 | 50 | sp->use_tasklet = true; |
63b94509 | 51 | |
d0ebbc0c BS |
52 | sp->psp_irq = sp_pci->msix_entry[0].vector; |
53 | sp->ccp_irq = (sp_pci->msix_count > 1) ? sp_pci->msix_entry[1].vector | |
54 | : sp_pci->msix_entry[0].vector; | |
63b94509 | 55 | return 0; |
63b94509 TL |
56 | } |
57 | ||
d0ebbc0c | 58 | static int sp_get_msi_irq(struct sp_device *sp) |
63b94509 | 59 | { |
f4d18d65 | 60 | struct device *dev = sp->dev; |
c6c59bf2 | 61 | struct pci_dev *pdev = to_pci_dev(dev); |
63b94509 TL |
62 | int ret; |
63 | ||
64 | ret = pci_enable_msi(pdev); | |
65 | if (ret) | |
66 | return ret; | |
67 | ||
f4d18d65 BS |
68 | sp->ccp_irq = pdev->irq; |
69 | sp->psp_irq = pdev->irq; | |
63b94509 TL |
70 | |
71 | return 0; | |
63b94509 TL |
72 | } |
73 | ||
d0ebbc0c | 74 | static int sp_get_irqs(struct sp_device *sp) |
63b94509 | 75 | { |
f4d18d65 | 76 | struct device *dev = sp->dev; |
63b94509 TL |
77 | int ret; |
78 | ||
d0ebbc0c | 79 | ret = sp_get_msix_irqs(sp); |
63b94509 TL |
80 | if (!ret) |
81 | return 0; | |
82 | ||
83 | /* Couldn't get MSI-X vectors, try MSI */ | |
84 | dev_notice(dev, "could not enable MSI-X (%d), trying MSI\n", ret); | |
d0ebbc0c | 85 | ret = sp_get_msi_irq(sp); |
63b94509 TL |
86 | if (!ret) |
87 | return 0; | |
88 | ||
89 | /* Couldn't get MSI interrupt */ | |
90 | dev_notice(dev, "could not enable MSI (%d)\n", ret); | |
91 | ||
92 | return ret; | |
93 | } | |
94 | ||
d0ebbc0c | 95 | static void sp_free_irqs(struct sp_device *sp) |
63b94509 | 96 | { |
d0ebbc0c | 97 | struct sp_pci *sp_pci = sp->dev_specific; |
f4d18d65 | 98 | struct device *dev = sp->dev; |
c6c59bf2 | 99 | struct pci_dev *pdev = to_pci_dev(dev); |
63b94509 | 100 | |
d0ebbc0c | 101 | if (sp_pci->msix_count) |
63b94509 | 102 | pci_disable_msix(pdev); |
f4d18d65 | 103 | else if (sp->psp_irq) |
63b94509 | 104 | pci_disable_msi(pdev); |
f4d18d65 BS |
105 | |
106 | sp->ccp_irq = 0; | |
107 | sp->psp_irq = 0; | |
63b94509 TL |
108 | } |
109 | ||
2a6170df BS |
110 | static bool sp_pci_is_master(struct sp_device *sp) |
111 | { | |
112 | struct device *dev_cur, *dev_new; | |
113 | struct pci_dev *pdev_cur, *pdev_new; | |
114 | ||
115 | dev_new = sp->dev; | |
116 | dev_cur = sp_dev_master->dev; | |
117 | ||
118 | pdev_new = to_pci_dev(dev_new); | |
119 | pdev_cur = to_pci_dev(dev_cur); | |
120 | ||
121 | if (pdev_new->bus->number < pdev_cur->bus->number) | |
122 | return true; | |
123 | ||
124 | if (PCI_SLOT(pdev_new->devfn) < PCI_SLOT(pdev_cur->devfn)) | |
125 | return true; | |
126 | ||
127 | if (PCI_FUNC(pdev_new->devfn) < PCI_FUNC(pdev_cur->devfn)) | |
128 | return true; | |
129 | ||
130 | return false; | |
131 | } | |
132 | ||
133 | static void psp_set_master(struct sp_device *sp) | |
134 | { | |
135 | if (!sp_dev_master) { | |
136 | sp_dev_master = sp; | |
137 | return; | |
138 | } | |
139 | ||
140 | if (sp_pci_is_master(sp)) | |
141 | sp_dev_master = sp; | |
142 | } | |
143 | ||
144 | static struct sp_device *psp_get_master(void) | |
145 | { | |
146 | return sp_dev_master; | |
147 | } | |
148 | ||
d0ebbc0c | 149 | static int sp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) |
63b94509 | 150 | { |
720419f0 | 151 | struct sp_device *sp; |
d0ebbc0c | 152 | struct sp_pci *sp_pci; |
63b94509 | 153 | struct device *dev = &pdev->dev; |
970e8303 BS |
154 | void __iomem * const *iomap_table; |
155 | int bar_mask; | |
63b94509 TL |
156 | int ret; |
157 | ||
158 | ret = -ENOMEM; | |
720419f0 BS |
159 | sp = sp_alloc_struct(dev); |
160 | if (!sp) | |
63b94509 TL |
161 | goto e_err; |
162 | ||
d0ebbc0c BS |
163 | sp_pci = devm_kzalloc(dev, sizeof(*sp_pci), GFP_KERNEL); |
164 | if (!sp_pci) | |
be03a3a0 TL |
165 | goto e_err; |
166 | ||
d0ebbc0c | 167 | sp->dev_specific = sp_pci; |
720419f0 BS |
168 | sp->dev_vdata = (struct sp_dev_vdata *)id->driver_data; |
169 | if (!sp->dev_vdata) { | |
c7019c4d GH |
170 | ret = -ENODEV; |
171 | dev_err(dev, "missing driver data\n"); | |
172 | goto e_err; | |
173 | } | |
63b94509 | 174 | |
970e8303 | 175 | ret = pcim_enable_device(pdev); |
63b94509 | 176 | if (ret) { |
970e8303 | 177 | dev_err(dev, "pcim_enable_device failed (%d)\n", ret); |
be03a3a0 | 178 | goto e_err; |
63b94509 TL |
179 | } |
180 | ||
970e8303 BS |
181 | bar_mask = pci_select_bars(pdev, IORESOURCE_MEM); |
182 | ret = pcim_iomap_regions(pdev, bar_mask, "ccp"); | |
63b94509 | 183 | if (ret) { |
970e8303 BS |
184 | dev_err(dev, "pcim_iomap_regions failed (%d)\n", ret); |
185 | goto e_err; | |
63b94509 TL |
186 | } |
187 | ||
970e8303 BS |
188 | iomap_table = pcim_iomap_table(pdev); |
189 | if (!iomap_table) { | |
190 | dev_err(dev, "pcim_iomap_table failed\n"); | |
191 | ret = -ENOMEM; | |
192 | goto e_err; | |
193 | } | |
63b94509 | 194 | |
720419f0 BS |
195 | sp->io_map = iomap_table[sp->dev_vdata->bar]; |
196 | if (!sp->io_map) { | |
970e8303 BS |
197 | dev_err(dev, "ioremap failed\n"); |
198 | ret = -ENOMEM; | |
199 | goto e_err; | |
63b94509 | 200 | } |
970e8303 | 201 | |
d0ebbc0c | 202 | ret = sp_get_irqs(sp); |
f4d18d65 BS |
203 | if (ret) |
204 | goto e_err; | |
205 | ||
970e8303 | 206 | pci_set_master(pdev); |
2a6170df BS |
207 | sp->set_psp_master_device = psp_set_master; |
208 | sp->get_psp_master_device = psp_get_master; | |
63b94509 | 209 | |
3d77565b TL |
210 | ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); |
211 | if (ret) { | |
212 | ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); | |
63b94509 | 213 | if (ret) { |
3d77565b | 214 | dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", |
63b94509 | 215 | ret); |
970e8303 | 216 | goto e_err; |
63b94509 TL |
217 | } |
218 | } | |
219 | ||
720419f0 | 220 | dev_set_drvdata(dev, sp); |
63b94509 | 221 | |
720419f0 | 222 | ret = sp_init(sp); |
63b94509 | 223 | if (ret) |
970e8303 | 224 | goto e_err; |
63b94509 | 225 | |
63b94509 TL |
226 | return 0; |
227 | ||
63b94509 TL |
228 | e_err: |
229 | dev_notice(dev, "initialization failed\n"); | |
230 | return ret; | |
231 | } | |
232 | ||
d0ebbc0c | 233 | static void sp_pci_remove(struct pci_dev *pdev) |
63b94509 TL |
234 | { |
235 | struct device *dev = &pdev->dev; | |
720419f0 | 236 | struct sp_device *sp = dev_get_drvdata(dev); |
63b94509 | 237 | |
720419f0 | 238 | if (!sp) |
db34cf91 TL |
239 | return; |
240 | ||
720419f0 | 241 | sp_destroy(sp); |
63b94509 | 242 | |
d0ebbc0c | 243 | sp_free_irqs(sp); |
63b94509 TL |
244 | } |
245 | ||
246 | #ifdef CONFIG_PM | |
d0ebbc0c | 247 | static int sp_pci_suspend(struct pci_dev *pdev, pm_message_t state) |
63b94509 TL |
248 | { |
249 | struct device *dev = &pdev->dev; | |
720419f0 | 250 | struct sp_device *sp = dev_get_drvdata(dev); |
63b94509 | 251 | |
720419f0 | 252 | return sp_suspend(sp, state); |
63b94509 TL |
253 | } |
254 | ||
d0ebbc0c | 255 | static int sp_pci_resume(struct pci_dev *pdev) |
63b94509 TL |
256 | { |
257 | struct device *dev = &pdev->dev; | |
720419f0 | 258 | struct sp_device *sp = dev_get_drvdata(dev); |
63b94509 | 259 | |
720419f0 | 260 | return sp_resume(sp); |
63b94509 TL |
261 | } |
262 | #endif | |
263 | ||
2a6170df | 264 | #ifdef CONFIG_CRYPTO_DEV_SP_PSP |
dcbc0c6e | 265 | static const struct psp_vdata pspv1 = { |
ad01a984 TL |
266 | .cmdresp_reg = 0x10580, |
267 | .cmdbuff_addr_lo_reg = 0x105e0, | |
268 | .cmdbuff_addr_hi_reg = 0x105e4, | |
269 | .feature_reg = 0x105fc, | |
270 | .inten_reg = 0x10610, | |
271 | .intsts_reg = 0x10614, | |
2a6170df | 272 | }; |
dcbc0c6e TL |
273 | |
274 | static const struct psp_vdata pspv2 = { | |
275 | .cmdresp_reg = 0x10980, | |
276 | .cmdbuff_addr_lo_reg = 0x109e0, | |
277 | .cmdbuff_addr_hi_reg = 0x109e4, | |
278 | .feature_reg = 0x109fc, | |
279 | .inten_reg = 0x10690, | |
280 | .intsts_reg = 0x10694, | |
281 | }; | |
2a6170df BS |
282 | #endif |
283 | ||
720419f0 | 284 | static const struct sp_dev_vdata dev_vdata[] = { |
dcbc0c6e | 285 | { /* 0 */ |
720419f0 BS |
286 | .bar = 2, |
287 | #ifdef CONFIG_CRYPTO_DEV_SP_CCP | |
288 | .ccp_vdata = &ccpv3, | |
289 | #endif | |
290 | }, | |
dcbc0c6e | 291 | { /* 1 */ |
720419f0 BS |
292 | .bar = 2, |
293 | #ifdef CONFIG_CRYPTO_DEV_SP_CCP | |
294 | .ccp_vdata = &ccpv5a, | |
2a6170df BS |
295 | #endif |
296 | #ifdef CONFIG_CRYPTO_DEV_SP_PSP | |
dcbc0c6e | 297 | .psp_vdata = &pspv1, |
720419f0 BS |
298 | #endif |
299 | }, | |
dcbc0c6e | 300 | { /* 2 */ |
720419f0 BS |
301 | .bar = 2, |
302 | #ifdef CONFIG_CRYPTO_DEV_SP_CCP | |
303 | .ccp_vdata = &ccpv5b, | |
dcbc0c6e TL |
304 | #endif |
305 | }, | |
306 | { /* 3 */ | |
307 | .bar = 2, | |
308 | #ifdef CONFIG_CRYPTO_DEV_SP_CCP | |
309 | .ccp_vdata = &ccpv5a, | |
310 | #endif | |
311 | #ifdef CONFIG_CRYPTO_DEV_SP_PSP | |
312 | .psp_vdata = &pspv2, | |
720419f0 BS |
313 | #endif |
314 | }, | |
315 | }; | |
d0ebbc0c | 316 | static const struct pci_device_id sp_pci_table[] = { |
720419f0 BS |
317 | { PCI_VDEVICE(AMD, 0x1537), (kernel_ulong_t)&dev_vdata[0] }, |
318 | { PCI_VDEVICE(AMD, 0x1456), (kernel_ulong_t)&dev_vdata[1] }, | |
319 | { PCI_VDEVICE(AMD, 0x1468), (kernel_ulong_t)&dev_vdata[2] }, | |
dcbc0c6e | 320 | { PCI_VDEVICE(AMD, 0x1486), (kernel_ulong_t)&dev_vdata[3] }, |
63b94509 TL |
321 | /* Last entry must be zero */ |
322 | { 0, } | |
323 | }; | |
d0ebbc0c | 324 | MODULE_DEVICE_TABLE(pci, sp_pci_table); |
63b94509 | 325 | |
d0ebbc0c | 326 | static struct pci_driver sp_pci_driver = { |
166db195 | 327 | .name = "ccp", |
d0ebbc0c BS |
328 | .id_table = sp_pci_table, |
329 | .probe = sp_pci_probe, | |
330 | .remove = sp_pci_remove, | |
63b94509 | 331 | #ifdef CONFIG_PM |
d0ebbc0c BS |
332 | .suspend = sp_pci_suspend, |
333 | .resume = sp_pci_resume, | |
63b94509 TL |
334 | #endif |
335 | }; | |
336 | ||
d0ebbc0c | 337 | int sp_pci_init(void) |
63b94509 | 338 | { |
d0ebbc0c | 339 | return pci_register_driver(&sp_pci_driver); |
63b94509 TL |
340 | } |
341 | ||
d0ebbc0c | 342 | void sp_pci_exit(void) |
63b94509 | 343 | { |
d0ebbc0c | 344 | pci_unregister_driver(&sp_pci_driver); |
63b94509 | 345 | } |