GNU Linux-libre 4.14.266-gnu1
[releases.git] / drivers / crypto / ccp / sp-platform.c
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 }