Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[linux-2.6-microblaze.git] / drivers / char / tpm / tpm_tis_spi_main.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2015 Infineon Technologies AG
4  * Copyright (C) 2016 STMicroelectronics SAS
5  *
6  * Authors:
7  * Peter Huewe <peter.huewe@infineon.com>
8  * Christophe Ricard <christophe-h.ricard@st.com>
9  *
10  * Maintained by: <tpmdd-devel@lists.sourceforge.net>
11  *
12  * Device driver for TCG/TCPA TPM (trusted platform module).
13  * Specifications at www.trustedcomputinggroup.org
14  *
15  * This device driver implements the TPM interface as defined in
16  * the TCG TPM Interface Spec version 1.3, revision 27 via _raw/native
17  * SPI access_.
18  *
19  * It is based on the original tpm_tis device driver from Leendert van
20  * Dorn and Kyleen Hall and Jarko Sakkinnen.
21  */
22
23 #include <linux/acpi.h>
24 #include <linux/completion.h>
25 #include <linux/init.h>
26 #include <linux/interrupt.h>
27 #include <linux/kernel.h>
28 #include <linux/module.h>
29 #include <linux/slab.h>
30
31 #include <linux/of_device.h>
32 #include <linux/spi/spi.h>
33 #include <linux/tpm.h>
34
35 #include "tpm.h"
36 #include "tpm_tis_core.h"
37 #include "tpm_tis_spi.h"
38
39 #define MAX_SPI_FRAMESIZE 64
40
41 /*
42  * TCG SPI flow control is documented in section 6.4 of the spec[1]. In short,
43  * keep trying to read from the device until MISO goes high indicating the
44  * wait state has ended.
45  *
46  * [1] https://trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/
47  */
48 static int tpm_tis_spi_flow_control(struct tpm_tis_spi_phy *phy,
49                                     struct spi_transfer *spi_xfer)
50 {
51         struct spi_message m;
52         int ret, i;
53
54         if ((phy->iobuf[3] & 0x01) == 0) {
55                 // handle SPI wait states
56                 phy->iobuf[0] = 0;
57
58                 for (i = 0; i < TPM_RETRY; i++) {
59                         spi_xfer->len = 1;
60                         spi_message_init(&m);
61                         spi_message_add_tail(spi_xfer, &m);
62                         ret = spi_sync_locked(phy->spi_device, &m);
63                         if (ret < 0)
64                                 return ret;
65                         if (phy->iobuf[0] & 0x01)
66                                 break;
67                 }
68
69                 if (i == TPM_RETRY)
70                         return -ETIMEDOUT;
71         }
72
73         return 0;
74 }
75
76 int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
77                          u8 *in, const u8 *out)
78 {
79         struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data);
80         int ret = 0;
81         struct spi_message m;
82         struct spi_transfer spi_xfer;
83         u8 transfer_len;
84
85         spi_bus_lock(phy->spi_device->master);
86
87         while (len) {
88                 transfer_len = min_t(u16, len, MAX_SPI_FRAMESIZE);
89
90                 phy->iobuf[0] = (in ? 0x80 : 0) | (transfer_len - 1);
91                 phy->iobuf[1] = 0xd4;
92                 phy->iobuf[2] = addr >> 8;
93                 phy->iobuf[3] = addr;
94
95                 memset(&spi_xfer, 0, sizeof(spi_xfer));
96                 spi_xfer.tx_buf = phy->iobuf;
97                 spi_xfer.rx_buf = phy->iobuf;
98                 spi_xfer.len = 4;
99                 spi_xfer.cs_change = 1;
100
101                 spi_message_init(&m);
102                 spi_message_add_tail(&spi_xfer, &m);
103                 ret = spi_sync_locked(phy->spi_device, &m);
104                 if (ret < 0)
105                         goto exit;
106
107                 ret = phy->flow_control(phy, &spi_xfer);
108                 if (ret < 0)
109                         goto exit;
110
111                 spi_xfer.cs_change = 0;
112                 spi_xfer.len = transfer_len;
113                 spi_xfer.delay.value = 5;
114                 spi_xfer.delay.unit = SPI_DELAY_UNIT_USECS;
115
116                 if (in) {
117                         spi_xfer.tx_buf = NULL;
118                 } else if (out) {
119                         spi_xfer.rx_buf = NULL;
120                         memcpy(phy->iobuf, out, transfer_len);
121                         out += transfer_len;
122                 }
123
124                 spi_message_init(&m);
125                 spi_message_add_tail(&spi_xfer, &m);
126                 reinit_completion(&phy->ready);
127                 ret = spi_sync_locked(phy->spi_device, &m);
128                 if (ret < 0)
129                         goto exit;
130
131                 if (in) {
132                         memcpy(in, phy->iobuf, transfer_len);
133                         in += transfer_len;
134                 }
135
136                 len -= transfer_len;
137         }
138
139 exit:
140         spi_bus_unlock(phy->spi_device->master);
141         return ret;
142 }
143
144 static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
145                                   u16 len, u8 *result)
146 {
147         return tpm_tis_spi_transfer(data, addr, len, result, NULL);
148 }
149
150 static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr,
151                                    u16 len, const u8 *value)
152 {
153         return tpm_tis_spi_transfer(data, addr, len, NULL, value);
154 }
155
156 int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
157 {
158         __le16 result_le;
159         int rc;
160
161         rc = data->phy_ops->read_bytes(data, addr, sizeof(u16),
162                                        (u8 *)&result_le);
163         if (!rc)
164                 *result = le16_to_cpu(result_le);
165
166         return rc;
167 }
168
169 int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result)
170 {
171         __le32 result_le;
172         int rc;
173
174         rc = data->phy_ops->read_bytes(data, addr, sizeof(u32),
175                                        (u8 *)&result_le);
176         if (!rc)
177                 *result = le32_to_cpu(result_le);
178
179         return rc;
180 }
181
182 int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value)
183 {
184         __le32 value_le;
185         int rc;
186
187         value_le = cpu_to_le32(value);
188         rc = data->phy_ops->write_bytes(data, addr, sizeof(u32),
189                                         (u8 *)&value_le);
190
191         return rc;
192 }
193
194 int tpm_tis_spi_init(struct spi_device *spi, struct tpm_tis_spi_phy *phy,
195                      int irq, const struct tpm_tis_phy_ops *phy_ops)
196 {
197         phy->iobuf = devm_kmalloc(&spi->dev, MAX_SPI_FRAMESIZE, GFP_KERNEL);
198         if (!phy->iobuf)
199                 return -ENOMEM;
200
201         phy->spi_device = spi;
202
203         return tpm_tis_core_init(&spi->dev, &phy->priv, irq, phy_ops, NULL);
204 }
205
206 static const struct tpm_tis_phy_ops tpm_spi_phy_ops = {
207         .read_bytes = tpm_tis_spi_read_bytes,
208         .write_bytes = tpm_tis_spi_write_bytes,
209         .read16 = tpm_tis_spi_read16,
210         .read32 = tpm_tis_spi_read32,
211         .write32 = tpm_tis_spi_write32,
212 };
213
214 static int tpm_tis_spi_probe(struct spi_device *dev)
215 {
216         struct tpm_tis_spi_phy *phy;
217         int irq;
218
219         phy = devm_kzalloc(&dev->dev, sizeof(struct tpm_tis_spi_phy),
220                            GFP_KERNEL);
221         if (!phy)
222                 return -ENOMEM;
223
224         phy->flow_control = tpm_tis_spi_flow_control;
225
226         /* If the SPI device has an IRQ then use that */
227         if (dev->irq > 0)
228                 irq = dev->irq;
229         else
230                 irq = -1;
231
232         init_completion(&phy->ready);
233         return tpm_tis_spi_init(dev, phy, irq, &tpm_spi_phy_ops);
234 }
235
236 typedef int (*tpm_tis_spi_probe_func)(struct spi_device *);
237
238 static int tpm_tis_spi_driver_probe(struct spi_device *spi)
239 {
240         const struct spi_device_id *spi_dev_id = spi_get_device_id(spi);
241         tpm_tis_spi_probe_func probe_func;
242
243         probe_func = of_device_get_match_data(&spi->dev);
244         if (!probe_func && spi_dev_id)
245                 probe_func = (tpm_tis_spi_probe_func)spi_dev_id->driver_data;
246         if (!probe_func)
247                 return -ENODEV;
248
249         return probe_func(spi);
250 }
251
252 static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_spi_resume);
253
254 static int tpm_tis_spi_remove(struct spi_device *dev)
255 {
256         struct tpm_chip *chip = spi_get_drvdata(dev);
257
258         tpm_chip_unregister(chip);
259         tpm_tis_remove(chip);
260         return 0;
261 }
262
263 static const struct spi_device_id tpm_tis_spi_id[] = {
264         { "tpm_tis_spi", (unsigned long)tpm_tis_spi_probe },
265         { "cr50", (unsigned long)cr50_spi_probe },
266         {}
267 };
268 MODULE_DEVICE_TABLE(spi, tpm_tis_spi_id);
269
270 static const struct of_device_id of_tis_spi_match[] = {
271         { .compatible = "st,st33htpm-spi", .data = tpm_tis_spi_probe },
272         { .compatible = "infineon,slb9670", .data = tpm_tis_spi_probe },
273         { .compatible = "tcg,tpm_tis-spi", .data = tpm_tis_spi_probe },
274         { .compatible = "google,cr50", .data = cr50_spi_probe },
275         {}
276 };
277 MODULE_DEVICE_TABLE(of, of_tis_spi_match);
278
279 static const struct acpi_device_id acpi_tis_spi_match[] = {
280         {"SMO0768", 0},
281         {}
282 };
283 MODULE_DEVICE_TABLE(acpi, acpi_tis_spi_match);
284
285 static struct spi_driver tpm_tis_spi_driver = {
286         .driver = {
287                 .name = "tpm_tis_spi",
288                 .pm = &tpm_tis_pm,
289                 .of_match_table = of_match_ptr(of_tis_spi_match),
290                 .acpi_match_table = ACPI_PTR(acpi_tis_spi_match),
291         },
292         .probe = tpm_tis_spi_driver_probe,
293         .remove = tpm_tis_spi_remove,
294         .id_table = tpm_tis_spi_id,
295 };
296 module_spi_driver(tpm_tis_spi_driver);
297
298 MODULE_DESCRIPTION("TPM Driver for native SPI access");
299 MODULE_LICENSE("GPL");