]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
c4f4b325 | 2 | /* |
d0ebbc0c | 3 | * AMD Secure Processor device driver |
c4f4b325 | 4 | * |
fa5cd1c7 | 5 | * Copyright (C) 2014,2018 Advanced Micro Devices, Inc. |
c4f4b325 TL |
6 | * |
7 | * Author: Tom Lendacky <thomas.lendacky@amd.com> | |
c4f4b325 TL |
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> | |
126ae9ad | 22 | #include <linux/of.h> |
6c506343 TL |
23 | #include <linux/of_address.h> |
24 | #include <linux/acpi.h> | |
c4f4b325 TL |
25 | |
26 | #include "ccp-dev.h" | |
27 | ||
d0ebbc0c | 28 | struct sp_platform { |
6c506343 | 29 | int coherent; |
f4d18d65 | 30 | unsigned int irq_count; |
6c506343 TL |
31 | }; |
32 | ||
3512dcb4 NC |
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 | |
c7019c4d | 58 | |
d0ebbc0c | 59 | static struct sp_dev_vdata *sp_get_of_version(struct platform_device *pdev) |
c7019c4d GH |
60 | { |
61 | #ifdef CONFIG_OF | |
62 | const struct of_device_id *match; | |
63 | ||
d0ebbc0c | 64 | match = of_match_node(sp_of_match, pdev->dev.of_node); |
c7019c4d | 65 | if (match && match->data) |
720419f0 | 66 | return (struct sp_dev_vdata *)match->data; |
c7019c4d | 67 | #endif |
9d1fb196 | 68 | return NULL; |
c7019c4d GH |
69 | } |
70 | ||
d0ebbc0c | 71 | static struct sp_dev_vdata *sp_get_acpi_version(struct platform_device *pdev) |
c7019c4d GH |
72 | { |
73 | #ifdef CONFIG_ACPI | |
74 | const struct acpi_device_id *match; | |
75 | ||
d0ebbc0c | 76 | match = acpi_match_device(sp_acpi_match, &pdev->dev); |
c7019c4d | 77 | if (match && match->driver_data) |
720419f0 | 78 | return (struct sp_dev_vdata *)match->driver_data; |
c7019c4d | 79 | #endif |
9d1fb196 | 80 | return NULL; |
c7019c4d GH |
81 | } |
82 | ||
d0ebbc0c | 83 | static int sp_get_irqs(struct sp_device *sp) |
c4f4b325 | 84 | { |
d0ebbc0c | 85 | struct sp_platform *sp_platform = sp->dev_specific; |
f4d18d65 | 86 | struct device *dev = sp->dev; |
c6c59bf2 | 87 | struct platform_device *pdev = to_platform_device(dev); |
f4d18d65 | 88 | unsigned int i, count; |
c4f4b325 TL |
89 | int ret; |
90 | ||
f4d18d65 BS |
91 | for (i = 0, count = 0; i < pdev->num_resources; i++) { |
92 | struct resource *res = &pdev->resource[i]; | |
93 | ||
94 | if (resource_type(res) == IORESOURCE_IRQ) | |
95 | count++; | |
96 | } | |
97 | ||
d0ebbc0c | 98 | sp_platform->irq_count = count; |
f4d18d65 | 99 | |
c4f4b325 | 100 | ret = platform_get_irq(pdev, 0); |
28a2cc67 GS |
101 | if (ret < 0) { |
102 | dev_notice(dev, "unable to get IRQ (%d)\n", ret); | |
c4f4b325 | 103 | return ret; |
28a2cc67 | 104 | } |
c4f4b325 | 105 | |
f4d18d65 BS |
106 | sp->psp_irq = ret; |
107 | if (count == 1) { | |
108 | sp->ccp_irq = ret; | |
109 | } else { | |
110 | ret = platform_get_irq(pdev, 1); | |
111 | if (ret < 0) { | |
112 | dev_notice(dev, "unable to get IRQ (%d)\n", ret); | |
113 | return ret; | |
114 | } | |
115 | ||
116 | sp->ccp_irq = ret; | |
c4f4b325 TL |
117 | } |
118 | ||
119 | return 0; | |
120 | } | |
121 | ||
d0ebbc0c | 122 | static int sp_platform_probe(struct platform_device *pdev) |
c4f4b325 | 123 | { |
720419f0 | 124 | struct sp_device *sp; |
d0ebbc0c | 125 | struct sp_platform *sp_platform; |
c4f4b325 | 126 | struct device *dev = &pdev->dev; |
1831eff8 | 127 | enum dev_dma_attr attr; |
c4f4b325 TL |
128 | struct resource *ior; |
129 | int ret; | |
130 | ||
131 | ret = -ENOMEM; | |
720419f0 BS |
132 | sp = sp_alloc_struct(dev); |
133 | if (!sp) | |
c4f4b325 TL |
134 | goto e_err; |
135 | ||
d0ebbc0c BS |
136 | sp_platform = devm_kzalloc(dev, sizeof(*sp_platform), GFP_KERNEL); |
137 | if (!sp_platform) | |
6c506343 TL |
138 | goto e_err; |
139 | ||
d0ebbc0c BS |
140 | sp->dev_specific = sp_platform; |
141 | sp->dev_vdata = pdev->dev.of_node ? sp_get_of_version(pdev) | |
142 | : sp_get_acpi_version(pdev); | |
720419f0 | 143 | if (!sp->dev_vdata) { |
c7019c4d GH |
144 | ret = -ENODEV; |
145 | dev_err(dev, "missing driver data\n"); | |
146 | goto e_err; | |
147 | } | |
c4f4b325 | 148 | |
970e8303 | 149 | ior = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
720419f0 BS |
150 | sp->io_map = devm_ioremap_resource(dev, ior); |
151 | if (IS_ERR(sp->io_map)) { | |
152 | ret = PTR_ERR(sp->io_map); | |
be03a3a0 | 153 | goto e_err; |
c4f4b325 | 154 | } |
c4f4b325 | 155 | |
1831eff8 SS |
156 | attr = device_get_dma_attr(dev); |
157 | if (attr == DEV_DMA_NOT_SUPPORTED) { | |
158 | dev_err(dev, "DMA is not supported"); | |
be03a3a0 | 159 | goto e_err; |
261bf074 | 160 | } |
c4f4b325 | 161 | |
d0ebbc0c BS |
162 | sp_platform->coherent = (attr == DEV_DMA_COHERENT); |
163 | if (sp_platform->coherent) | |
720419f0 | 164 | sp->axcache = CACHE_WB_NO_ALLOC; |
126ae9ad | 165 | else |
720419f0 | 166 | sp->axcache = CACHE_NONE; |
126ae9ad | 167 | |
1831eff8 SS |
168 | ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); |
169 | if (ret) { | |
170 | dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", ret); | |
171 | goto e_err; | |
172 | } | |
173 | ||
d0ebbc0c | 174 | ret = sp_get_irqs(sp); |
f4d18d65 BS |
175 | if (ret) |
176 | goto e_err; | |
177 | ||
720419f0 | 178 | dev_set_drvdata(dev, sp); |
c4f4b325 | 179 | |
720419f0 | 180 | ret = sp_init(sp); |
c4f4b325 | 181 | if (ret) |
be03a3a0 | 182 | goto e_err; |
c4f4b325 TL |
183 | |
184 | dev_notice(dev, "enabled\n"); | |
185 | ||
186 | return 0; | |
187 | ||
c4f4b325 TL |
188 | e_err: |
189 | dev_notice(dev, "initialization failed\n"); | |
190 | return ret; | |
191 | } | |
192 | ||
d0ebbc0c | 193 | static int sp_platform_remove(struct platform_device *pdev) |
c4f4b325 TL |
194 | { |
195 | struct device *dev = &pdev->dev; | |
720419f0 | 196 | struct sp_device *sp = dev_get_drvdata(dev); |
c4f4b325 | 197 | |
720419f0 | 198 | sp_destroy(sp); |
c4f4b325 | 199 | |
c4f4b325 TL |
200 | dev_notice(dev, "disabled\n"); |
201 | ||
202 | return 0; | |
203 | } | |
204 | ||
205 | #ifdef CONFIG_PM | |
d0ebbc0c | 206 | static int sp_platform_suspend(struct platform_device *pdev, |
c4f4b325 TL |
207 | pm_message_t state) |
208 | { | |
209 | struct device *dev = &pdev->dev; | |
720419f0 | 210 | struct sp_device *sp = dev_get_drvdata(dev); |
c4f4b325 | 211 | |
720419f0 | 212 | return sp_suspend(sp, state); |
c4f4b325 TL |
213 | } |
214 | ||
d0ebbc0c | 215 | static int sp_platform_resume(struct platform_device *pdev) |
c4f4b325 TL |
216 | { |
217 | struct device *dev = &pdev->dev; | |
720419f0 | 218 | struct sp_device *sp = dev_get_drvdata(dev); |
c4f4b325 | 219 | |
720419f0 | 220 | return sp_resume(sp); |
c4f4b325 TL |
221 | } |
222 | #endif | |
223 | ||
d0ebbc0c | 224 | static struct platform_driver sp_platform_driver = { |
c4f4b325 | 225 | .driver = { |
166db195 | 226 | .name = "ccp", |
6c506343 | 227 | #ifdef CONFIG_ACPI |
d0ebbc0c | 228 | .acpi_match_table = sp_acpi_match, |
6c506343 TL |
229 | #endif |
230 | #ifdef CONFIG_OF | |
d0ebbc0c | 231 | .of_match_table = sp_of_match, |
6c506343 | 232 | #endif |
c4f4b325 | 233 | }, |
d0ebbc0c BS |
234 | .probe = sp_platform_probe, |
235 | .remove = sp_platform_remove, | |
c4f4b325 | 236 | #ifdef CONFIG_PM |
d0ebbc0c BS |
237 | .suspend = sp_platform_suspend, |
238 | .resume = sp_platform_resume, | |
c4f4b325 TL |
239 | #endif |
240 | }; | |
241 | ||
d0ebbc0c | 242 | int sp_platform_init(void) |
c4f4b325 | 243 | { |
d0ebbc0c | 244 | return platform_driver_register(&sp_platform_driver); |
c4f4b325 TL |
245 | } |
246 | ||
d0ebbc0c | 247 | void sp_platform_exit(void) |
c4f4b325 | 248 | { |
d0ebbc0c | 249 | platform_driver_unregister(&sp_platform_driver); |
c4f4b325 | 250 | } |