1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * AMD Secure Processor device driver |
4 | * |
5 | * Copyright (C) 2014,2018 Advanced Micro Devices, Inc. |
6 | * |
7 | * Author: Tom Lendacky <thomas.lendacky@amd.com> |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/device.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/ioport.h> |
15 | #include <linux/dma-mapping.h> |
16 | #include <linux/kthread.h> |
17 | #include <linux/sched.h> |
18 | #include <linux/interrupt.h> |
19 | #include <linux/spinlock.h> |
20 | #include <linux/delay.h> |
21 | #include <linux/ccp.h> |
22 | #include <linux/of.h> |
23 | #include <linux/of_address.h> |
24 | #include <linux/acpi.h> |
25 | |
26 | #include "ccp-dev.h" |
27 | |
28 | struct sp_platform { |
29 | int coherent; |
30 | unsigned int irq_count; |
31 | }; |
32 | |
33 | static const struct sp_dev_vdata dev_vdata[] = { |
34 | { |
35 | .bar = 0, |
36 | #ifdef CONFIG_CRYPTO_DEV_SP_CCP |
37 | .ccp_vdata = &ccpv3_platform, |
38 | #endif |
39 | }, |
40 | }; |
41 | |
42 | #ifdef CONFIG_ACPI |
43 | static const struct acpi_device_id sp_acpi_match[] = { |
44 | { "AMDI0C00" , (kernel_ulong_t)&dev_vdata[0] }, |
45 | { }, |
46 | }; |
47 | MODULE_DEVICE_TABLE(acpi, sp_acpi_match); |
48 | #endif |
49 | |
50 | #ifdef CONFIG_OF |
51 | static const struct of_device_id sp_of_match[] = { |
52 | { .compatible = "amd,ccp-seattle-v1a" , |
53 | .data = (const void *)&dev_vdata[0] }, |
54 | { }, |
55 | }; |
56 | MODULE_DEVICE_TABLE(of, sp_of_match); |
57 | #endif |
58 | |
59 | static struct sp_dev_vdata *sp_get_of_version(struct platform_device *pdev) |
60 | { |
61 | #ifdef CONFIG_OF |
62 | const struct of_device_id *match; |
63 | |
64 | match = of_match_node(matches: sp_of_match, node: pdev->dev.of_node); |
65 | if (match && match->data) |
66 | return (struct sp_dev_vdata *)match->data; |
67 | #endif |
68 | return NULL; |
69 | } |
70 | |
71 | static struct sp_dev_vdata *sp_get_acpi_version(struct platform_device *pdev) |
72 | { |
73 | #ifdef CONFIG_ACPI |
74 | const struct acpi_device_id *match; |
75 | |
76 | match = acpi_match_device(ids: sp_acpi_match, dev: &pdev->dev); |
77 | if (match && match->driver_data) |
78 | return (struct sp_dev_vdata *)match->driver_data; |
79 | #endif |
80 | return NULL; |
81 | } |
82 | |
83 | static int sp_get_irqs(struct sp_device *sp) |
84 | { |
85 | struct sp_platform *sp_platform = sp->dev_specific; |
86 | struct device *dev = sp->dev; |
87 | struct platform_device *pdev = to_platform_device(dev); |
88 | int ret; |
89 | |
90 | sp_platform->irq_count = platform_irq_count(pdev); |
91 | |
92 | ret = platform_get_irq(pdev, 0); |
93 | if (ret < 0) { |
94 | dev_notice(dev, "unable to get IRQ (%d)\n" , ret); |
95 | return ret; |
96 | } |
97 | |
98 | sp->psp_irq = ret; |
99 | if (sp_platform->irq_count == 1) { |
100 | sp->ccp_irq = ret; |
101 | } else { |
102 | ret = platform_get_irq(pdev, 1); |
103 | if (ret < 0) { |
104 | dev_notice(dev, "unable to get IRQ (%d)\n" , ret); |
105 | return ret; |
106 | } |
107 | |
108 | sp->ccp_irq = ret; |
109 | } |
110 | |
111 | return 0; |
112 | } |
113 | |
114 | static int sp_platform_probe(struct platform_device *pdev) |
115 | { |
116 | struct sp_device *sp; |
117 | struct sp_platform *sp_platform; |
118 | struct device *dev = &pdev->dev; |
119 | enum dev_dma_attr attr; |
120 | int ret; |
121 | |
122 | ret = -ENOMEM; |
123 | sp = sp_alloc_struct(dev); |
124 | if (!sp) |
125 | goto e_err; |
126 | |
127 | sp_platform = devm_kzalloc(dev, size: sizeof(*sp_platform), GFP_KERNEL); |
128 | if (!sp_platform) |
129 | goto e_err; |
130 | |
131 | sp->dev_specific = sp_platform; |
132 | sp->dev_vdata = pdev->dev.of_node ? sp_get_of_version(pdev) |
133 | : sp_get_acpi_version(pdev); |
134 | if (!sp->dev_vdata) { |
135 | ret = -ENODEV; |
136 | dev_err(dev, "missing driver data\n" ); |
137 | goto e_err; |
138 | } |
139 | |
140 | sp->io_map = devm_platform_ioremap_resource(pdev, index: 0); |
141 | if (IS_ERR(ptr: sp->io_map)) { |
142 | ret = PTR_ERR(ptr: sp->io_map); |
143 | goto e_err; |
144 | } |
145 | |
146 | attr = device_get_dma_attr(dev); |
147 | if (attr == DEV_DMA_NOT_SUPPORTED) { |
148 | dev_err(dev, "DMA is not supported" ); |
149 | goto e_err; |
150 | } |
151 | |
152 | sp_platform->coherent = (attr == DEV_DMA_COHERENT); |
153 | if (sp_platform->coherent) |
154 | sp->axcache = CACHE_WB_NO_ALLOC; |
155 | else |
156 | sp->axcache = CACHE_NONE; |
157 | |
158 | ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); |
159 | if (ret) { |
160 | dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n" , ret); |
161 | goto e_err; |
162 | } |
163 | |
164 | ret = sp_get_irqs(sp); |
165 | if (ret) |
166 | goto e_err; |
167 | |
168 | dev_set_drvdata(dev, data: sp); |
169 | |
170 | ret = sp_init(sp); |
171 | if (ret) |
172 | goto e_err; |
173 | |
174 | dev_notice(dev, "enabled\n" ); |
175 | |
176 | return 0; |
177 | |
178 | e_err: |
179 | dev_notice(dev, "initialization failed\n" ); |
180 | return ret; |
181 | } |
182 | |
183 | static void sp_platform_remove(struct platform_device *pdev) |
184 | { |
185 | struct device *dev = &pdev->dev; |
186 | struct sp_device *sp = dev_get_drvdata(dev); |
187 | |
188 | sp_destroy(sp); |
189 | |
190 | dev_notice(dev, "disabled\n" ); |
191 | } |
192 | |
193 | #ifdef CONFIG_PM |
194 | static int sp_platform_suspend(struct platform_device *pdev, |
195 | pm_message_t state) |
196 | { |
197 | struct device *dev = &pdev->dev; |
198 | struct sp_device *sp = dev_get_drvdata(dev); |
199 | |
200 | return sp_suspend(sp); |
201 | } |
202 | |
203 | static int sp_platform_resume(struct platform_device *pdev) |
204 | { |
205 | struct device *dev = &pdev->dev; |
206 | struct sp_device *sp = dev_get_drvdata(dev); |
207 | |
208 | return sp_resume(sp); |
209 | } |
210 | #endif |
211 | |
212 | static struct platform_driver sp_platform_driver = { |
213 | .driver = { |
214 | .name = "ccp" , |
215 | #ifdef CONFIG_ACPI |
216 | .acpi_match_table = sp_acpi_match, |
217 | #endif |
218 | #ifdef CONFIG_OF |
219 | .of_match_table = sp_of_match, |
220 | #endif |
221 | }, |
222 | .probe = sp_platform_probe, |
223 | .remove_new = sp_platform_remove, |
224 | #ifdef CONFIG_PM |
225 | .suspend = sp_platform_suspend, |
226 | .resume = sp_platform_resume, |
227 | #endif |
228 | }; |
229 | |
230 | int sp_platform_init(void) |
231 | { |
232 | return platform_driver_register(&sp_platform_driver); |
233 | } |
234 | |
235 | void sp_platform_exit(void) |
236 | { |
237 | platform_driver_unregister(&sp_platform_driver); |
238 | } |
239 | |