ccp-platform.c 6.3 KB

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