1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * AMD Secure Processor driver |
4 | * |
5 | * Copyright (C) 2017-2018 Advanced Micro Devices, Inc. |
6 | * |
7 | * Author: Tom Lendacky <thomas.lendacky@amd.com> |
8 | * Author: Gary R Hook <gary.hook@amd.com> |
9 | * Author: Brijesh Singh <brijesh.singh@amd.com> |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/kthread.h> |
15 | #include <linux/sched.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/spinlock.h> |
18 | #include <linux/spinlock_types.h> |
19 | #include <linux/types.h> |
20 | #include <linux/ccp.h> |
21 | |
22 | #include "ccp-dev.h" |
23 | #include "sp-dev.h" |
24 | |
25 | MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>" ); |
26 | MODULE_AUTHOR("Gary R Hook <gary.hook@amd.com>" ); |
27 | MODULE_LICENSE("GPL" ); |
28 | MODULE_VERSION("1.1.0" ); |
29 | MODULE_DESCRIPTION("AMD Secure Processor driver" ); |
30 | |
31 | /* List of SPs, SP count, read-write access lock, and access functions |
32 | * |
33 | * Lock structure: get sp_unit_lock for reading whenever we need to |
34 | * examine the SP list. |
35 | */ |
36 | static DEFINE_RWLOCK(sp_unit_lock); |
37 | static LIST_HEAD(sp_units); |
38 | |
39 | /* Ever-increasing value to produce unique unit numbers */ |
40 | static atomic_t sp_ordinal; |
41 | |
42 | static void sp_add_device(struct sp_device *sp) |
43 | { |
44 | unsigned long flags; |
45 | |
46 | write_lock_irqsave(&sp_unit_lock, flags); |
47 | |
48 | list_add_tail(new: &sp->entry, head: &sp_units); |
49 | |
50 | write_unlock_irqrestore(&sp_unit_lock, flags); |
51 | } |
52 | |
53 | static void sp_del_device(struct sp_device *sp) |
54 | { |
55 | unsigned long flags; |
56 | |
57 | write_lock_irqsave(&sp_unit_lock, flags); |
58 | |
59 | list_del(entry: &sp->entry); |
60 | |
61 | write_unlock_irqrestore(&sp_unit_lock, flags); |
62 | } |
63 | |
64 | static irqreturn_t sp_irq_handler(int irq, void *data) |
65 | { |
66 | struct sp_device *sp = data; |
67 | |
68 | if (sp->ccp_irq_handler) |
69 | sp->ccp_irq_handler(irq, sp->ccp_irq_data); |
70 | |
71 | if (sp->psp_irq_handler) |
72 | sp->psp_irq_handler(irq, sp->psp_irq_data); |
73 | |
74 | return IRQ_HANDLED; |
75 | } |
76 | |
77 | int sp_request_ccp_irq(struct sp_device *sp, irq_handler_t handler, |
78 | const char *name, void *data) |
79 | { |
80 | int ret; |
81 | |
82 | if ((sp->psp_irq == sp->ccp_irq) && sp->dev_vdata->psp_vdata) { |
83 | /* Need a common routine to manage all interrupts */ |
84 | sp->ccp_irq_data = data; |
85 | sp->ccp_irq_handler = handler; |
86 | |
87 | if (!sp->irq_registered) { |
88 | ret = request_irq(irq: sp->ccp_irq, handler: sp_irq_handler, flags: 0, |
89 | name: sp->name, dev: sp); |
90 | if (ret) |
91 | return ret; |
92 | |
93 | sp->irq_registered = true; |
94 | } |
95 | } else { |
96 | /* Each sub-device can manage it's own interrupt */ |
97 | ret = request_irq(irq: sp->ccp_irq, handler, flags: 0, name, dev: data); |
98 | if (ret) |
99 | return ret; |
100 | } |
101 | |
102 | return 0; |
103 | } |
104 | |
105 | int sp_request_psp_irq(struct sp_device *sp, irq_handler_t handler, |
106 | const char *name, void *data) |
107 | { |
108 | int ret; |
109 | |
110 | if ((sp->psp_irq == sp->ccp_irq) && sp->dev_vdata->ccp_vdata) { |
111 | /* Need a common routine to manage all interrupts */ |
112 | sp->psp_irq_data = data; |
113 | sp->psp_irq_handler = handler; |
114 | |
115 | if (!sp->irq_registered) { |
116 | ret = request_irq(irq: sp->psp_irq, handler: sp_irq_handler, flags: 0, |
117 | name: sp->name, dev: sp); |
118 | if (ret) |
119 | return ret; |
120 | |
121 | sp->irq_registered = true; |
122 | } |
123 | } else { |
124 | /* Each sub-device can manage it's own interrupt */ |
125 | ret = request_irq(irq: sp->psp_irq, handler, flags: 0, name, dev: data); |
126 | if (ret) |
127 | return ret; |
128 | } |
129 | |
130 | return 0; |
131 | } |
132 | |
133 | void sp_free_ccp_irq(struct sp_device *sp, void *data) |
134 | { |
135 | if ((sp->psp_irq == sp->ccp_irq) && sp->dev_vdata->psp_vdata) { |
136 | /* Using common routine to manage all interrupts */ |
137 | if (!sp->psp_irq_handler) { |
138 | /* Nothing else using it, so free it */ |
139 | free_irq(sp->ccp_irq, sp); |
140 | |
141 | sp->irq_registered = false; |
142 | } |
143 | |
144 | sp->ccp_irq_handler = NULL; |
145 | sp->ccp_irq_data = NULL; |
146 | } else { |
147 | /* Each sub-device can manage it's own interrupt */ |
148 | free_irq(sp->ccp_irq, data); |
149 | } |
150 | } |
151 | |
152 | void sp_free_psp_irq(struct sp_device *sp, void *data) |
153 | { |
154 | if ((sp->psp_irq == sp->ccp_irq) && sp->dev_vdata->ccp_vdata) { |
155 | /* Using common routine to manage all interrupts */ |
156 | if (!sp->ccp_irq_handler) { |
157 | /* Nothing else using it, so free it */ |
158 | free_irq(sp->psp_irq, sp); |
159 | |
160 | sp->irq_registered = false; |
161 | } |
162 | |
163 | sp->psp_irq_handler = NULL; |
164 | sp->psp_irq_data = NULL; |
165 | } else { |
166 | /* Each sub-device can manage it's own interrupt */ |
167 | free_irq(sp->psp_irq, data); |
168 | } |
169 | } |
170 | |
171 | /** |
172 | * sp_alloc_struct - allocate and initialize the sp_device struct |
173 | * |
174 | * @dev: device struct of the SP |
175 | */ |
176 | struct sp_device *sp_alloc_struct(struct device *dev) |
177 | { |
178 | struct sp_device *sp; |
179 | |
180 | sp = devm_kzalloc(dev, size: sizeof(*sp), GFP_KERNEL); |
181 | if (!sp) |
182 | return NULL; |
183 | |
184 | sp->dev = dev; |
185 | sp->ord = atomic_inc_return(v: &sp_ordinal); |
186 | snprintf(buf: sp->name, SP_MAX_NAME_LEN, fmt: "sp-%u" , sp->ord); |
187 | |
188 | return sp; |
189 | } |
190 | |
191 | int sp_init(struct sp_device *sp) |
192 | { |
193 | sp_add_device(sp); |
194 | |
195 | if (sp->dev_vdata->ccp_vdata) |
196 | ccp_dev_init(sp); |
197 | |
198 | if (sp->dev_vdata->psp_vdata) |
199 | psp_dev_init(sp); |
200 | return 0; |
201 | } |
202 | |
203 | void sp_destroy(struct sp_device *sp) |
204 | { |
205 | if (sp->dev_vdata->ccp_vdata) |
206 | ccp_dev_destroy(sp); |
207 | |
208 | if (sp->dev_vdata->psp_vdata) |
209 | psp_dev_destroy(sp); |
210 | |
211 | sp_del_device(sp); |
212 | } |
213 | |
214 | int sp_suspend(struct sp_device *sp) |
215 | { |
216 | if (sp->dev_vdata->ccp_vdata) { |
217 | ccp_dev_suspend(sp); |
218 | } |
219 | |
220 | return 0; |
221 | } |
222 | |
223 | int sp_resume(struct sp_device *sp) |
224 | { |
225 | if (sp->dev_vdata->ccp_vdata) { |
226 | ccp_dev_resume(sp); |
227 | } |
228 | |
229 | return 0; |
230 | } |
231 | |
232 | struct sp_device *sp_get_psp_master_device(void) |
233 | { |
234 | struct sp_device *i, *ret = NULL; |
235 | unsigned long flags; |
236 | |
237 | write_lock_irqsave(&sp_unit_lock, flags); |
238 | if (list_empty(head: &sp_units)) |
239 | goto unlock; |
240 | |
241 | list_for_each_entry(i, &sp_units, entry) { |
242 | if (i->psp_data && i->get_psp_master_device) { |
243 | ret = i->get_psp_master_device(); |
244 | break; |
245 | } |
246 | } |
247 | |
248 | unlock: |
249 | write_unlock_irqrestore(&sp_unit_lock, flags); |
250 | return ret; |
251 | } |
252 | |
253 | static int __init sp_mod_init(void) |
254 | { |
255 | #ifdef CONFIG_X86 |
256 | int ret; |
257 | |
258 | ret = sp_pci_init(); |
259 | if (ret) |
260 | return ret; |
261 | |
262 | #ifdef CONFIG_CRYPTO_DEV_SP_PSP |
263 | psp_pci_init(); |
264 | #endif |
265 | |
266 | return 0; |
267 | #endif |
268 | |
269 | #ifdef CONFIG_ARM64 |
270 | int ret; |
271 | |
272 | ret = sp_platform_init(); |
273 | if (ret) |
274 | return ret; |
275 | |
276 | return 0; |
277 | #endif |
278 | |
279 | return -ENODEV; |
280 | } |
281 | |
282 | static void __exit sp_mod_exit(void) |
283 | { |
284 | #ifdef CONFIG_X86 |
285 | |
286 | #ifdef CONFIG_CRYPTO_DEV_SP_PSP |
287 | psp_pci_exit(); |
288 | #endif |
289 | |
290 | sp_pci_exit(); |
291 | #endif |
292 | |
293 | #ifdef CONFIG_ARM64 |
294 | sp_platform_exit(); |
295 | #endif |
296 | } |
297 | |
298 | module_init(sp_mod_init); |
299 | module_exit(sp_mod_exit); |
300 | |