18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * AMD Secure Processor device driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014,2018 Advanced Micro Devices, Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Tom Lendacky <thomas.lendacky@amd.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/ioport.h> 158c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 168c2ecf20Sopenharmony_ci#include <linux/kthread.h> 178c2ecf20Sopenharmony_ci#include <linux/sched.h> 188c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 198c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 208c2ecf20Sopenharmony_ci#include <linux/delay.h> 218c2ecf20Sopenharmony_ci#include <linux/ccp.h> 228c2ecf20Sopenharmony_ci#include <linux/of.h> 238c2ecf20Sopenharmony_ci#include <linux/of_address.h> 248c2ecf20Sopenharmony_ci#include <linux/acpi.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "ccp-dev.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct sp_platform { 298c2ecf20Sopenharmony_ci int coherent; 308c2ecf20Sopenharmony_ci unsigned int irq_count; 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic const struct sp_dev_vdata dev_vdata[] = { 348c2ecf20Sopenharmony_ci { 358c2ecf20Sopenharmony_ci .bar = 0, 368c2ecf20Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_SP_CCP 378c2ecf20Sopenharmony_ci .ccp_vdata = &ccpv3_platform, 388c2ecf20Sopenharmony_ci#endif 398c2ecf20Sopenharmony_ci }, 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 438c2ecf20Sopenharmony_cistatic const struct acpi_device_id sp_acpi_match[] = { 448c2ecf20Sopenharmony_ci { "AMDI0C00", (kernel_ulong_t)&dev_vdata[0] }, 458c2ecf20Sopenharmony_ci { }, 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, sp_acpi_match); 488c2ecf20Sopenharmony_ci#endif 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 518c2ecf20Sopenharmony_cistatic const struct of_device_id sp_of_match[] = { 528c2ecf20Sopenharmony_ci { .compatible = "amd,ccp-seattle-v1a", 538c2ecf20Sopenharmony_ci .data = (const void *)&dev_vdata[0] }, 548c2ecf20Sopenharmony_ci { }, 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sp_of_match); 578c2ecf20Sopenharmony_ci#endif 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic struct sp_dev_vdata *sp_get_of_version(struct platform_device *pdev) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 628c2ecf20Sopenharmony_ci const struct of_device_id *match; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci match = of_match_node(sp_of_match, pdev->dev.of_node); 658c2ecf20Sopenharmony_ci if (match && match->data) 668c2ecf20Sopenharmony_ci return (struct sp_dev_vdata *)match->data; 678c2ecf20Sopenharmony_ci#endif 688c2ecf20Sopenharmony_ci return NULL; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic struct sp_dev_vdata *sp_get_acpi_version(struct platform_device *pdev) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 748c2ecf20Sopenharmony_ci const struct acpi_device_id *match; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci match = acpi_match_device(sp_acpi_match, &pdev->dev); 778c2ecf20Sopenharmony_ci if (match && match->driver_data) 788c2ecf20Sopenharmony_ci return (struct sp_dev_vdata *)match->driver_data; 798c2ecf20Sopenharmony_ci#endif 808c2ecf20Sopenharmony_ci return NULL; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int sp_get_irqs(struct sp_device *sp) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct sp_platform *sp_platform = sp->dev_specific; 868c2ecf20Sopenharmony_ci struct device *dev = sp->dev; 878c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 888c2ecf20Sopenharmony_ci unsigned int i, count; 898c2ecf20Sopenharmony_ci int ret; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci for (i = 0, count = 0; i < pdev->num_resources; i++) { 928c2ecf20Sopenharmony_ci struct resource *res = &pdev->resource[i]; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (resource_type(res) == IORESOURCE_IRQ) 958c2ecf20Sopenharmony_ci count++; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci sp_platform->irq_count = count; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci ret = platform_get_irq(pdev, 0); 1018c2ecf20Sopenharmony_ci if (ret < 0) { 1028c2ecf20Sopenharmony_ci dev_notice(dev, "unable to get IRQ (%d)\n", ret); 1038c2ecf20Sopenharmony_ci return ret; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci sp->psp_irq = ret; 1078c2ecf20Sopenharmony_ci if (count == 1) { 1088c2ecf20Sopenharmony_ci sp->ccp_irq = ret; 1098c2ecf20Sopenharmony_ci } else { 1108c2ecf20Sopenharmony_ci ret = platform_get_irq(pdev, 1); 1118c2ecf20Sopenharmony_ci if (ret < 0) { 1128c2ecf20Sopenharmony_ci dev_notice(dev, "unable to get IRQ (%d)\n", ret); 1138c2ecf20Sopenharmony_ci return ret; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci sp->ccp_irq = ret; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int sp_platform_probe(struct platform_device *pdev) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct sp_device *sp; 1258c2ecf20Sopenharmony_ci struct sp_platform *sp_platform; 1268c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1278c2ecf20Sopenharmony_ci enum dev_dma_attr attr; 1288c2ecf20Sopenharmony_ci int ret; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci ret = -ENOMEM; 1318c2ecf20Sopenharmony_ci sp = sp_alloc_struct(dev); 1328c2ecf20Sopenharmony_ci if (!sp) 1338c2ecf20Sopenharmony_ci goto e_err; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci sp_platform = devm_kzalloc(dev, sizeof(*sp_platform), GFP_KERNEL); 1368c2ecf20Sopenharmony_ci if (!sp_platform) 1378c2ecf20Sopenharmony_ci goto e_err; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci sp->dev_specific = sp_platform; 1408c2ecf20Sopenharmony_ci sp->dev_vdata = pdev->dev.of_node ? sp_get_of_version(pdev) 1418c2ecf20Sopenharmony_ci : sp_get_acpi_version(pdev); 1428c2ecf20Sopenharmony_ci if (!sp->dev_vdata) { 1438c2ecf20Sopenharmony_ci ret = -ENODEV; 1448c2ecf20Sopenharmony_ci dev_err(dev, "missing driver data\n"); 1458c2ecf20Sopenharmony_ci goto e_err; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci sp->io_map = devm_platform_ioremap_resource(pdev, 0); 1498c2ecf20Sopenharmony_ci if (IS_ERR(sp->io_map)) { 1508c2ecf20Sopenharmony_ci ret = PTR_ERR(sp->io_map); 1518c2ecf20Sopenharmony_ci goto e_err; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci attr = device_get_dma_attr(dev); 1558c2ecf20Sopenharmony_ci if (attr == DEV_DMA_NOT_SUPPORTED) { 1568c2ecf20Sopenharmony_ci dev_err(dev, "DMA is not supported"); 1578c2ecf20Sopenharmony_ci goto e_err; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci sp_platform->coherent = (attr == DEV_DMA_COHERENT); 1618c2ecf20Sopenharmony_ci if (sp_platform->coherent) 1628c2ecf20Sopenharmony_ci sp->axcache = CACHE_WB_NO_ALLOC; 1638c2ecf20Sopenharmony_ci else 1648c2ecf20Sopenharmony_ci sp->axcache = CACHE_NONE; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); 1678c2ecf20Sopenharmony_ci if (ret) { 1688c2ecf20Sopenharmony_ci dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", ret); 1698c2ecf20Sopenharmony_ci goto e_err; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci ret = sp_get_irqs(sp); 1738c2ecf20Sopenharmony_ci if (ret) 1748c2ecf20Sopenharmony_ci goto e_err; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci dev_set_drvdata(dev, sp); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci ret = sp_init(sp); 1798c2ecf20Sopenharmony_ci if (ret) 1808c2ecf20Sopenharmony_ci goto e_err; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci dev_notice(dev, "enabled\n"); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return 0; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cie_err: 1878c2ecf20Sopenharmony_ci dev_notice(dev, "initialization failed\n"); 1888c2ecf20Sopenharmony_ci return ret; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int sp_platform_remove(struct platform_device *pdev) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1948c2ecf20Sopenharmony_ci struct sp_device *sp = dev_get_drvdata(dev); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci sp_destroy(sp); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci dev_notice(dev, "disabled\n"); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 2048c2ecf20Sopenharmony_cistatic int sp_platform_suspend(struct platform_device *pdev, 2058c2ecf20Sopenharmony_ci pm_message_t state) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2088c2ecf20Sopenharmony_ci struct sp_device *sp = dev_get_drvdata(dev); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return sp_suspend(sp); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int sp_platform_resume(struct platform_device *pdev) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2168c2ecf20Sopenharmony_ci struct sp_device *sp = dev_get_drvdata(dev); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return sp_resume(sp); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci#endif 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic struct platform_driver sp_platform_driver = { 2238c2ecf20Sopenharmony_ci .driver = { 2248c2ecf20Sopenharmony_ci .name = "ccp", 2258c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 2268c2ecf20Sopenharmony_ci .acpi_match_table = sp_acpi_match, 2278c2ecf20Sopenharmony_ci#endif 2288c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 2298c2ecf20Sopenharmony_ci .of_match_table = sp_of_match, 2308c2ecf20Sopenharmony_ci#endif 2318c2ecf20Sopenharmony_ci }, 2328c2ecf20Sopenharmony_ci .probe = sp_platform_probe, 2338c2ecf20Sopenharmony_ci .remove = sp_platform_remove, 2348c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 2358c2ecf20Sopenharmony_ci .suspend = sp_platform_suspend, 2368c2ecf20Sopenharmony_ci .resume = sp_platform_resume, 2378c2ecf20Sopenharmony_ci#endif 2388c2ecf20Sopenharmony_ci}; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ciint sp_platform_init(void) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci return platform_driver_register(&sp_platform_driver); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_civoid sp_platform_exit(void) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci platform_driver_unregister(&sp_platform_driver); 2488c2ecf20Sopenharmony_ci} 249