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.11-rc1 293 lines 6.4 kB view raw
1/* 2 * AMD Cryptographic Coprocessor (CCP) driver 3 * 4 * Copyright (C) 2014,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/platform_device.h> 17#include <linux/ioport.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#include <linux/of.h> 26#include <linux/of_address.h> 27#include <linux/acpi.h> 28 29#include "ccp-dev.h" 30 31struct ccp_platform { 32 int coherent; 33}; 34 35static const struct acpi_device_id ccp_acpi_match[]; 36static const struct of_device_id ccp_of_match[]; 37 38static struct ccp_vdata *ccp_get_of_version(struct platform_device *pdev) 39{ 40#ifdef CONFIG_OF 41 const struct of_device_id *match; 42 43 match = of_match_node(ccp_of_match, pdev->dev.of_node); 44 if (match && match->data) 45 return (struct ccp_vdata *)match->data; 46#endif 47 return 0; 48} 49 50static struct ccp_vdata *ccp_get_acpi_version(struct platform_device *pdev) 51{ 52#ifdef CONFIG_ACPI 53 const struct acpi_device_id *match; 54 55 match = acpi_match_device(ccp_acpi_match, &pdev->dev); 56 if (match && match->driver_data) 57 return (struct ccp_vdata *)match->driver_data; 58#endif 59 return 0; 60} 61 62static int ccp_get_irq(struct ccp_device *ccp) 63{ 64 struct device *dev = ccp->dev; 65 struct platform_device *pdev = to_platform_device(dev); 66 int ret; 67 68 ret = platform_get_irq(pdev, 0); 69 if (ret < 0) 70 return ret; 71 72 ccp->irq = ret; 73 ret = request_irq(ccp->irq, ccp->vdata->perform->irqhandler, 0, 74 ccp->name, dev); 75 if (ret) { 76 dev_notice(dev, "unable to allocate IRQ (%d)\n", ret); 77 return ret; 78 } 79 80 return 0; 81} 82 83static int ccp_get_irqs(struct ccp_device *ccp) 84{ 85 struct device *dev = ccp->dev; 86 int ret; 87 88 ret = ccp_get_irq(ccp); 89 if (!ret) 90 return 0; 91 92 /* Couldn't get an interrupt */ 93 dev_notice(dev, "could not enable interrupts (%d)\n", ret); 94 95 return ret; 96} 97 98static void ccp_free_irqs(struct ccp_device *ccp) 99{ 100 struct device *dev = ccp->dev; 101 102 free_irq(ccp->irq, dev); 103} 104 105static struct resource *ccp_find_mmio_area(struct ccp_device *ccp) 106{ 107 struct device *dev = ccp->dev; 108 struct platform_device *pdev = to_platform_device(dev); 109 struct resource *ior; 110 111 ior = platform_get_resource(pdev, IORESOURCE_MEM, 0); 112 if (ior && (resource_size(ior) >= 0x800)) 113 return ior; 114 115 return NULL; 116} 117 118static int ccp_platform_probe(struct platform_device *pdev) 119{ 120 struct ccp_device *ccp; 121 struct ccp_platform *ccp_platform; 122 struct device *dev = &pdev->dev; 123 enum dev_dma_attr attr; 124 struct resource *ior; 125 int ret; 126 127 ret = -ENOMEM; 128 ccp = ccp_alloc_struct(dev); 129 if (!ccp) 130 goto e_err; 131 132 ccp_platform = devm_kzalloc(dev, sizeof(*ccp_platform), GFP_KERNEL); 133 if (!ccp_platform) 134 goto e_err; 135 136 ccp->dev_specific = ccp_platform; 137 ccp->vdata = pdev->dev.of_node ? ccp_get_of_version(pdev) 138 : ccp_get_acpi_version(pdev); 139 if (!ccp->vdata || !ccp->vdata->version) { 140 ret = -ENODEV; 141 dev_err(dev, "missing driver data\n"); 142 goto e_err; 143 } 144 ccp->get_irq = ccp_get_irqs; 145 ccp->free_irq = ccp_free_irqs; 146 147 ior = ccp_find_mmio_area(ccp); 148 ccp->io_map = devm_ioremap_resource(dev, ior); 149 if (IS_ERR(ccp->io_map)) { 150 ret = PTR_ERR(ccp->io_map); 151 goto e_err; 152 } 153 ccp->io_regs = ccp->io_map; 154 155 attr = device_get_dma_attr(dev); 156 if (attr == DEV_DMA_NOT_SUPPORTED) { 157 dev_err(dev, "DMA is not supported"); 158 goto e_err; 159 } 160 161 ccp_platform->coherent = (attr == DEV_DMA_COHERENT); 162 if (ccp_platform->coherent) 163 ccp->axcache = CACHE_WB_NO_ALLOC; 164 else 165 ccp->axcache = CACHE_NONE; 166 167 ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); 168 if (ret) { 169 dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", ret); 170 goto e_err; 171 } 172 173 dev_set_drvdata(dev, ccp); 174 175 ret = ccp->vdata->perform->init(ccp); 176 if (ret) 177 goto e_err; 178 179 dev_notice(dev, "enabled\n"); 180 181 return 0; 182 183e_err: 184 dev_notice(dev, "initialization failed\n"); 185 return ret; 186} 187 188static int ccp_platform_remove(struct platform_device *pdev) 189{ 190 struct device *dev = &pdev->dev; 191 struct ccp_device *ccp = dev_get_drvdata(dev); 192 193 ccp->vdata->perform->destroy(ccp); 194 195 dev_notice(dev, "disabled\n"); 196 197 return 0; 198} 199 200#ifdef CONFIG_PM 201static int ccp_platform_suspend(struct platform_device *pdev, 202 pm_message_t state) 203{ 204 struct device *dev = &pdev->dev; 205 struct ccp_device *ccp = dev_get_drvdata(dev); 206 unsigned long flags; 207 unsigned int i; 208 209 spin_lock_irqsave(&ccp->cmd_lock, flags); 210 211 ccp->suspending = 1; 212 213 /* Wake all the queue kthreads to prepare for suspend */ 214 for (i = 0; i < ccp->cmd_q_count; i++) 215 wake_up_process(ccp->cmd_q[i].kthread); 216 217 spin_unlock_irqrestore(&ccp->cmd_lock, flags); 218 219 /* Wait for all queue kthreads to say they're done */ 220 while (!ccp_queues_suspended(ccp)) 221 wait_event_interruptible(ccp->suspend_queue, 222 ccp_queues_suspended(ccp)); 223 224 return 0; 225} 226 227static int ccp_platform_resume(struct platform_device *pdev) 228{ 229 struct device *dev = &pdev->dev; 230 struct ccp_device *ccp = dev_get_drvdata(dev); 231 unsigned long flags; 232 unsigned int i; 233 234 spin_lock_irqsave(&ccp->cmd_lock, flags); 235 236 ccp->suspending = 0; 237 238 /* Wake up all the kthreads */ 239 for (i = 0; i < ccp->cmd_q_count; i++) { 240 ccp->cmd_q[i].suspended = 0; 241 wake_up_process(ccp->cmd_q[i].kthread); 242 } 243 244 spin_unlock_irqrestore(&ccp->cmd_lock, flags); 245 246 return 0; 247} 248#endif 249 250#ifdef CONFIG_ACPI 251static const struct acpi_device_id ccp_acpi_match[] = { 252 { "AMDI0C00", (kernel_ulong_t)&ccpv3 }, 253 { }, 254}; 255MODULE_DEVICE_TABLE(acpi, ccp_acpi_match); 256#endif 257 258#ifdef CONFIG_OF 259static const struct of_device_id ccp_of_match[] = { 260 { .compatible = "amd,ccp-seattle-v1a", 261 .data = (const void *)&ccpv3 }, 262 { }, 263}; 264MODULE_DEVICE_TABLE(of, ccp_of_match); 265#endif 266 267static struct platform_driver ccp_platform_driver = { 268 .driver = { 269 .name = "ccp", 270#ifdef CONFIG_ACPI 271 .acpi_match_table = ccp_acpi_match, 272#endif 273#ifdef CONFIG_OF 274 .of_match_table = ccp_of_match, 275#endif 276 }, 277 .probe = ccp_platform_probe, 278 .remove = ccp_platform_remove, 279#ifdef CONFIG_PM 280 .suspend = ccp_platform_suspend, 281 .resume = ccp_platform_resume, 282#endif 283}; 284 285int ccp_platform_init(void) 286{ 287 return platform_driver_register(&ccp_platform_driver); 288} 289 290void ccp_platform_exit(void) 291{ 292 platform_driver_unregister(&ccp_platform_driver); 293}