1 /* 2 * AMD Secure Processor device 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 31 struct sp_platform { 32 int coherent; 33 unsigned int irq_count; 34 }; 35 36 static const struct acpi_device_id sp_acpi_match[]; 37 static const struct of_device_id sp_of_match[]; 38 39 static struct sp_dev_vdata *sp_get_of_version(struct platform_device *pdev) 40 { 41 #ifdef CONFIG_OF 42 const struct of_device_id *match; 43 44 match = of_match_node(sp_of_match, pdev->dev.of_node); 45 if (match && match->data) 46 return (struct sp_dev_vdata *)match->data; 47 #endif 48 return NULL; 49 } 50 51 static struct sp_dev_vdata *sp_get_acpi_version(struct platform_device *pdev) 52 { 53 #ifdef CONFIG_ACPI 54 const struct acpi_device_id *match; 55 56 match = acpi_match_device(sp_acpi_match, &pdev->dev); 57 if (match && match->driver_data) 58 return (struct sp_dev_vdata *)match->driver_data; 59 #endif 60 return NULL; 61 } 62 63 static int sp_get_irqs(struct sp_device *sp) 64 { 65 struct sp_platform *sp_platform = sp->dev_specific; 66 struct device *dev = sp->dev; 67 struct platform_device *pdev = to_platform_device(dev); 68 unsigned int i, count; 69 int ret; 70 71 for (i = 0, count = 0; i < pdev->num_resources; i++) { 72 struct resource *res = &pdev->resource[i]; 73 74 if (resource_type(res) == IORESOURCE_IRQ) 75 count++; 76 } 77 78 sp_platform->irq_count = count; 79 80 ret = platform_get_irq(pdev, 0); 81 if (ret < 0) { 82 dev_notice(dev, "unable to get IRQ (%d)\n", ret); 83 return ret; 84 } 85 86 sp->psp_irq = ret; 87 if (count == 1) { 88 sp->ccp_irq = ret; 89 } else { 90 ret = platform_get_irq(pdev, 1); 91 if (ret < 0) { 92 dev_notice(dev, "unable to get IRQ (%d)\n", ret); 93 return ret; 94 } 95 96 sp->ccp_irq = ret; 97 } 98 99 return 0; 100 } 101 102 static int sp_platform_probe(struct platform_device *pdev) 103 { 104 struct sp_device *sp; 105 struct sp_platform *sp_platform; 106 struct device *dev = &pdev->dev; 107 enum dev_dma_attr attr; 108 struct resource *ior; 109 int ret; 110 111 ret = -ENOMEM; 112 sp = sp_alloc_struct(dev); 113 if (!sp) 114 goto e_err; 115 116 sp_platform = devm_kzalloc(dev, sizeof(*sp_platform), GFP_KERNEL); 117 if (!sp_platform) 118 goto e_err; 119 120 sp->dev_specific = sp_platform; 121 sp->dev_vdata = pdev->dev.of_node ? sp_get_of_version(pdev) 122 : sp_get_acpi_version(pdev); 123 if (!sp->dev_vdata) { 124 ret = -ENODEV; 125 dev_err(dev, "missing driver data\n"); 126 goto e_err; 127 } 128 129 ior = platform_get_resource(pdev, IORESOURCE_MEM, 0); 130 sp->io_map = devm_ioremap_resource(dev, ior); 131 if (IS_ERR(sp->io_map)) { 132 ret = PTR_ERR(sp->io_map); 133 goto e_err; 134 } 135 136 attr = device_get_dma_attr(dev); 137 if (attr == DEV_DMA_NOT_SUPPORTED) { 138 dev_err(dev, "DMA is not supported"); 139 goto e_err; 140 } 141 142 sp_platform->coherent = (attr == DEV_DMA_COHERENT); 143 if (sp_platform->coherent) 144 sp->axcache = CACHE_WB_NO_ALLOC; 145 else 146 sp->axcache = CACHE_NONE; 147 148 ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); 149 if (ret) { 150 dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", ret); 151 goto e_err; 152 } 153 154 ret = sp_get_irqs(sp); 155 if (ret) 156 goto e_err; 157 158 dev_set_drvdata(dev, sp); 159 160 ret = sp_init(sp); 161 if (ret) 162 goto e_err; 163 164 dev_notice(dev, "enabled\n"); 165 166 return 0; 167 168 e_err: 169 dev_notice(dev, "initialization failed\n"); 170 return ret; 171 } 172 173 static int sp_platform_remove(struct platform_device *pdev) 174 { 175 struct device *dev = &pdev->dev; 176 struct sp_device *sp = dev_get_drvdata(dev); 177 178 sp_destroy(sp); 179 180 dev_notice(dev, "disabled\n"); 181 182 return 0; 183 } 184 185 #ifdef CONFIG_PM 186 static int sp_platform_suspend(struct platform_device *pdev, 187 pm_message_t state) 188 { 189 struct device *dev = &pdev->dev; 190 struct sp_device *sp = dev_get_drvdata(dev); 191 192 return sp_suspend(sp, state); 193 } 194 195 static int sp_platform_resume(struct platform_device *pdev) 196 { 197 struct device *dev = &pdev->dev; 198 struct sp_device *sp = dev_get_drvdata(dev); 199 200 return sp_resume(sp); 201 } 202 #endif 203 204 static const struct sp_dev_vdata dev_vdata[] = { 205 { 206 .bar = 0, 207 #ifdef CONFIG_CRYPTO_DEV_SP_CCP 208 .ccp_vdata = &ccpv3_platform, 209 #endif 210 }, 211 }; 212 213 #ifdef CONFIG_ACPI 214 static const struct acpi_device_id sp_acpi_match[] = { 215 { "AMDI0C00", (kernel_ulong_t)&dev_vdata[0] }, 216 { }, 217 }; 218 MODULE_DEVICE_TABLE(acpi, sp_acpi_match); 219 #endif 220 221 #ifdef CONFIG_OF 222 static const struct of_device_id sp_of_match[] = { 223 { .compatible = "amd,ccp-seattle-v1a", 224 .data = (const void *)&dev_vdata[0] }, 225 { }, 226 }; 227 MODULE_DEVICE_TABLE(of, sp_of_match); 228 #endif 229 230 static struct platform_driver sp_platform_driver = { 231 .driver = { 232 .name = "ccp", 233 #ifdef CONFIG_ACPI 234 .acpi_match_table = sp_acpi_match, 235 #endif 236 #ifdef CONFIG_OF 237 .of_match_table = sp_of_match, 238 #endif 239 }, 240 .probe = sp_platform_probe, 241 .remove = sp_platform_remove, 242 #ifdef CONFIG_PM 243 .suspend = sp_platform_suspend, 244 .resume = sp_platform_resume, 245 #endif 246 }; 247 248 int sp_platform_init(void) 249 { 250 return platform_driver_register(&sp_platform_driver); 251 } 252 253 void sp_platform_exit(void) 254 { 255 platform_driver_unregister(&sp_platform_driver); 256 } 257