Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.6 349 lines 7.3 kB view raw
1/* 2 * AMD Cryptographic Coprocessor (CCP) driver 3 * 4 * Copyright (C) 2013,2016 Advanced Micro Devices, Inc. 5 * 6 * Author: Tom Lendacky <thomas.lendacky@amd.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12 13#include <linux/module.h> 14#include <linux/kernel.h> 15#include <linux/device.h> 16#include <linux/pci.h> 17#include <linux/pci_ids.h> 18#include <linux/dma-mapping.h> 19#include <linux/kthread.h> 20#include <linux/sched.h> 21#include <linux/interrupt.h> 22#include <linux/spinlock.h> 23#include <linux/delay.h> 24#include <linux/ccp.h> 25 26#include "ccp-dev.h" 27 28#define IO_BAR 2 29#define IO_OFFSET 0x20000 30 31#define MSIX_VECTORS 2 32 33struct ccp_msix { 34 u32 vector; 35 char name[16]; 36}; 37 38struct ccp_pci { 39 int msix_count; 40 struct ccp_msix msix[MSIX_VECTORS]; 41}; 42 43static int ccp_get_msix_irqs(struct ccp_device *ccp) 44{ 45 struct ccp_pci *ccp_pci = ccp->dev_specific; 46 struct device *dev = ccp->dev; 47 struct pci_dev *pdev = to_pci_dev(dev); 48 struct msix_entry msix_entry[MSIX_VECTORS]; 49 unsigned int name_len = sizeof(ccp_pci->msix[0].name) - 1; 50 int v, ret; 51 52 for (v = 0; v < ARRAY_SIZE(msix_entry); v++) 53 msix_entry[v].entry = v; 54 55 ret = pci_enable_msix_range(pdev, msix_entry, 1, v); 56 if (ret < 0) 57 return ret; 58 59 ccp_pci->msix_count = ret; 60 for (v = 0; v < ccp_pci->msix_count; v++) { 61 /* Set the interrupt names and request the irqs */ 62 snprintf(ccp_pci->msix[v].name, name_len, "%s-%u", 63 ccp->name, v); 64 ccp_pci->msix[v].vector = msix_entry[v].vector; 65 ret = request_irq(ccp_pci->msix[v].vector, 66 ccp->vdata->perform->irqhandler, 67 0, ccp_pci->msix[v].name, dev); 68 if (ret) { 69 dev_notice(dev, "unable to allocate MSI-X IRQ (%d)\n", 70 ret); 71 goto e_irq; 72 } 73 } 74 75 return 0; 76 77e_irq: 78 while (v--) 79 free_irq(ccp_pci->msix[v].vector, dev); 80 81 pci_disable_msix(pdev); 82 83 ccp_pci->msix_count = 0; 84 85 return ret; 86} 87 88static int ccp_get_msi_irq(struct ccp_device *ccp) 89{ 90 struct device *dev = ccp->dev; 91 struct pci_dev *pdev = to_pci_dev(dev); 92 int ret; 93 94 ret = pci_enable_msi(pdev); 95 if (ret) 96 return ret; 97 98 ccp->irq = pdev->irq; 99 ret = request_irq(ccp->irq, ccp->vdata->perform->irqhandler, 0, 100 ccp->name, dev); 101 if (ret) { 102 dev_notice(dev, "unable to allocate MSI IRQ (%d)\n", ret); 103 goto e_msi; 104 } 105 106 return 0; 107 108e_msi: 109 pci_disable_msi(pdev); 110 111 return ret; 112} 113 114static int ccp_get_irqs(struct ccp_device *ccp) 115{ 116 struct device *dev = ccp->dev; 117 int ret; 118 119 ret = ccp_get_msix_irqs(ccp); 120 if (!ret) 121 return 0; 122 123 /* Couldn't get MSI-X vectors, try MSI */ 124 dev_notice(dev, "could not enable MSI-X (%d), trying MSI\n", ret); 125 ret = ccp_get_msi_irq(ccp); 126 if (!ret) 127 return 0; 128 129 /* Couldn't get MSI interrupt */ 130 dev_notice(dev, "could not enable MSI (%d)\n", ret); 131 132 return ret; 133} 134 135static void ccp_free_irqs(struct ccp_device *ccp) 136{ 137 struct ccp_pci *ccp_pci = ccp->dev_specific; 138 struct device *dev = ccp->dev; 139 struct pci_dev *pdev = to_pci_dev(dev); 140 141 if (ccp_pci->msix_count) { 142 while (ccp_pci->msix_count--) 143 free_irq(ccp_pci->msix[ccp_pci->msix_count].vector, 144 dev); 145 pci_disable_msix(pdev); 146 } else { 147 free_irq(ccp->irq, dev); 148 pci_disable_msi(pdev); 149 } 150} 151 152static int ccp_find_mmio_area(struct ccp_device *ccp) 153{ 154 struct device *dev = ccp->dev; 155 struct pci_dev *pdev = to_pci_dev(dev); 156 resource_size_t io_len; 157 unsigned long io_flags; 158 159 io_flags = pci_resource_flags(pdev, IO_BAR); 160 io_len = pci_resource_len(pdev, IO_BAR); 161 if ((io_flags & IORESOURCE_MEM) && (io_len >= (IO_OFFSET + 0x800))) 162 return IO_BAR; 163 164 return -EIO; 165} 166 167static int ccp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 168{ 169 struct ccp_device *ccp; 170 struct ccp_pci *ccp_pci; 171 struct device *dev = &pdev->dev; 172 unsigned int bar; 173 int ret; 174 175 ret = -ENOMEM; 176 ccp = ccp_alloc_struct(dev); 177 if (!ccp) 178 goto e_err; 179 180 ccp_pci = devm_kzalloc(dev, sizeof(*ccp_pci), GFP_KERNEL); 181 if (!ccp_pci) 182 goto e_err; 183 184 ccp->dev_specific = ccp_pci; 185 ccp->vdata = (struct ccp_vdata *)id->driver_data; 186 if (!ccp->vdata || !ccp->vdata->version) { 187 ret = -ENODEV; 188 dev_err(dev, "missing driver data\n"); 189 goto e_err; 190 } 191 ccp->get_irq = ccp_get_irqs; 192 ccp->free_irq = ccp_free_irqs; 193 194 ret = pci_request_regions(pdev, "ccp"); 195 if (ret) { 196 dev_err(dev, "pci_request_regions failed (%d)\n", ret); 197 goto e_err; 198 } 199 200 ret = pci_enable_device(pdev); 201 if (ret) { 202 dev_err(dev, "pci_enable_device failed (%d)\n", ret); 203 goto e_regions; 204 } 205 206 pci_set_master(pdev); 207 208 ret = ccp_find_mmio_area(ccp); 209 if (ret < 0) 210 goto e_device; 211 bar = ret; 212 213 ret = -EIO; 214 ccp->io_map = pci_iomap(pdev, bar, 0); 215 if (!ccp->io_map) { 216 dev_err(dev, "pci_iomap failed\n"); 217 goto e_device; 218 } 219 ccp->io_regs = ccp->io_map + IO_OFFSET; 220 221 ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); 222 if (ret) { 223 ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); 224 if (ret) { 225 dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", 226 ret); 227 goto e_iomap; 228 } 229 } 230 231 dev_set_drvdata(dev, ccp); 232 233 ret = ccp->vdata->perform->init(ccp); 234 if (ret) 235 goto e_iomap; 236 237 dev_notice(dev, "enabled\n"); 238 239 return 0; 240 241e_iomap: 242 pci_iounmap(pdev, ccp->io_map); 243 244e_device: 245 pci_disable_device(pdev); 246 247e_regions: 248 pci_release_regions(pdev); 249 250e_err: 251 dev_notice(dev, "initialization failed\n"); 252 return ret; 253} 254 255static void ccp_pci_remove(struct pci_dev *pdev) 256{ 257 struct device *dev = &pdev->dev; 258 struct ccp_device *ccp = dev_get_drvdata(dev); 259 260 if (!ccp) 261 return; 262 263 ccp->vdata->perform->destroy(ccp); 264 265 pci_iounmap(pdev, ccp->io_map); 266 267 pci_disable_device(pdev); 268 269 pci_release_regions(pdev); 270 271 dev_notice(dev, "disabled\n"); 272} 273 274#ifdef CONFIG_PM 275static int ccp_pci_suspend(struct pci_dev *pdev, pm_message_t state) 276{ 277 struct device *dev = &pdev->dev; 278 struct ccp_device *ccp = dev_get_drvdata(dev); 279 unsigned long flags; 280 unsigned int i; 281 282 spin_lock_irqsave(&ccp->cmd_lock, flags); 283 284 ccp->suspending = 1; 285 286 /* Wake all the queue kthreads to prepare for suspend */ 287 for (i = 0; i < ccp->cmd_q_count; i++) 288 wake_up_process(ccp->cmd_q[i].kthread); 289 290 spin_unlock_irqrestore(&ccp->cmd_lock, flags); 291 292 /* Wait for all queue kthreads to say they're done */ 293 while (!ccp_queues_suspended(ccp)) 294 wait_event_interruptible(ccp->suspend_queue, 295 ccp_queues_suspended(ccp)); 296 297 return 0; 298} 299 300static int ccp_pci_resume(struct pci_dev *pdev) 301{ 302 struct device *dev = &pdev->dev; 303 struct ccp_device *ccp = dev_get_drvdata(dev); 304 unsigned long flags; 305 unsigned int i; 306 307 spin_lock_irqsave(&ccp->cmd_lock, flags); 308 309 ccp->suspending = 0; 310 311 /* Wake up all the kthreads */ 312 for (i = 0; i < ccp->cmd_q_count; i++) { 313 ccp->cmd_q[i].suspended = 0; 314 wake_up_process(ccp->cmd_q[i].kthread); 315 } 316 317 spin_unlock_irqrestore(&ccp->cmd_lock, flags); 318 319 return 0; 320} 321#endif 322 323static const struct pci_device_id ccp_pci_table[] = { 324 { PCI_VDEVICE(AMD, 0x1537), (kernel_ulong_t)&ccpv3 }, 325 /* Last entry must be zero */ 326 { 0, } 327}; 328MODULE_DEVICE_TABLE(pci, ccp_pci_table); 329 330static struct pci_driver ccp_pci_driver = { 331 .name = "ccp", 332 .id_table = ccp_pci_table, 333 .probe = ccp_pci_probe, 334 .remove = ccp_pci_remove, 335#ifdef CONFIG_PM 336 .suspend = ccp_pci_suspend, 337 .resume = ccp_pci_resume, 338#endif 339}; 340 341int ccp_pci_init(void) 342{ 343 return pci_register_driver(&ccp_pci_driver); 344} 345 346void ccp_pci_exit(void) 347{ 348 pci_unregister_driver(&ccp_pci_driver); 349}