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 
sp_get_of_version(struct platform_device * pdev)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 
sp_get_acpi_version(struct platform_device * pdev)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 
sp_get_irqs(struct sp_device * sp)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 
sp_platform_probe(struct platform_device * pdev)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 
sp_platform_remove(struct platform_device * pdev)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
sp_platform_suspend(struct platform_device * pdev,pm_message_t state)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 
sp_platform_resume(struct platform_device * pdev)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 
sp_platform_init(void)248 int sp_platform_init(void)
249 {
250 	return platform_driver_register(&sp_platform_driver);
251 }
252 
sp_platform_exit(void)253 void sp_platform_exit(void)
254 {
255 	platform_driver_unregister(&sp_platform_driver);
256 }
257