]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
c01939c7 | 2 | /* |
07470d6f | 3 | * Copyright (C) 2013 Guntermann & Drunck, GmbH |
c01939c7 | 4 | * |
d38826a3 | 5 | * Written by Dirk Eibach <[email protected]> |
c01939c7 DE |
6 | */ |
7 | ||
4e4bf944 | 8 | #include <display_options.h> |
302c5dba | 9 | #include <dm.h> |
d677bfe2 | 10 | #include <tpm-v1.h> |
c01939c7 | 11 | #include <i2c.h> |
03de305e | 12 | #include <time.h> |
c01939c7 | 13 | #include <asm/unaligned.h> |
c05ed00a | 14 | #include <linux/delay.h> |
c01939c7 | 15 | |
302c5dba CR |
16 | #include "tpm_internal.h" |
17 | ||
c01939c7 DE |
18 | #define ATMEL_TPM_TIMEOUT_MS 5000 /* sufficient for anything but |
19 | generating/exporting keys */ | |
20 | ||
21 | /* | |
302c5dba | 22 | * tpm_atmel_twi_open() |
c01939c7 DE |
23 | * |
24 | * Requests access to locality 0 for the caller. After all commands have been | |
25 | * completed the caller is supposed to call tis_close(). | |
26 | * | |
27 | * Returns 0 on success, -1 on failure. | |
28 | */ | |
302c5dba | 29 | static int tpm_atmel_twi_open(struct udevice *dev) |
c01939c7 DE |
30 | { |
31 | return 0; | |
32 | } | |
33 | ||
34 | /* | |
302c5dba | 35 | * tpm_atmel_twi_close() |
c01939c7 DE |
36 | * |
37 | * terminate the currect session with the TPM by releasing the locked | |
38 | * locality. Returns 0 on success of -1 on failure (in case lock | |
39 | * removal did not succeed). | |
40 | */ | |
302c5dba CR |
41 | static int tpm_atmel_twi_close(struct udevice *dev) |
42 | { | |
43 | return 0; | |
44 | } | |
45 | ||
46 | /* | |
47 | * tpm_atmel_twi_get_desc() | |
48 | * | |
49 | * @dev: Device to check | |
50 | * @buf: Buffer to put the string | |
51 | * @size: Maximum size of buffer | |
185f812c | 52 | * Return: length of string, or -ENOSPC it no space |
302c5dba CR |
53 | */ |
54 | static int tpm_atmel_twi_get_desc(struct udevice *dev, char *buf, int size) | |
c01939c7 | 55 | { |
fb30d99d MM |
56 | if (size < 50) |
57 | return -ENOSPC; | |
58 | ||
59 | return snprintf(buf, size, "Atmel AT97SC3204T I2C 1.2 TPM (%s)", dev->name); | |
c01939c7 DE |
60 | } |
61 | ||
62 | /* | |
302c5dba | 63 | * tpm_atmel_twi_xfer() |
c01939c7 DE |
64 | * |
65 | * Send the requested data to the TPM and then try to get its response | |
66 | * | |
67 | * @sendbuf - buffer of the data to send | |
68 | * @send_size size of the data to send | |
69 | * @recvbuf - memory to save the response to | |
70 | * @recv_len - pointer to the size of the response buffer | |
71 | * | |
72 | * Returns 0 on success (and places the number of response bytes at recv_len) | |
73 | * or -1 on failure. | |
74 | */ | |
302c5dba CR |
75 | static int tpm_atmel_twi_xfer(struct udevice *dev, |
76 | const uint8_t *sendbuf, size_t send_size, | |
77 | uint8_t *recvbuf, size_t *recv_len) | |
c01939c7 DE |
78 | { |
79 | int res; | |
80 | unsigned long start; | |
81 | ||
82 | #ifdef DEBUG | |
83 | memset(recvbuf, 0xcc, *recv_len); | |
84 | printf("send to TPM (%d bytes, recv_len=%d):\n", send_size, *recv_len); | |
85 | print_buffer(0, (void *)sendbuf, 1, send_size, 0); | |
86 | #endif | |
87 | ||
03dcd410 | 88 | res = dm_i2c_write(dev, 0, sendbuf, send_size); |
c01939c7 DE |
89 | if (res) { |
90 | printf("i2c_write returned %d\n", res); | |
91 | return -1; | |
92 | } | |
93 | ||
94 | start = get_timer(0); | |
02f50d8e | 95 | |
03dcd410 | 96 | while ((res = dm_i2c_read(dev, 0, recvbuf, 10))) |
03dcd410 | 97 | { |
302c5dba | 98 | /* TODO Use TIS_TIMEOUT from tpm_tis_infineon.h */ |
c01939c7 DE |
99 | if (get_timer(start) > ATMEL_TPM_TIMEOUT_MS) { |
100 | puts("tpm timed out\n"); | |
101 | return -1; | |
102 | } | |
103 | udelay(100); | |
104 | } | |
105 | if (!res) { | |
b3f40703 JB |
106 | unsigned int hdr_recv_len; |
107 | hdr_recv_len = get_unaligned_be32(recvbuf + 2); | |
108 | if (hdr_recv_len < 10) { | |
109 | puts("tpm response header too small\n"); | |
110 | return -1; | |
111 | } else if (hdr_recv_len > *recv_len) { | |
112 | puts("tpm response length is bigger than receive buffer\n"); | |
113 | return -1; | |
114 | } else { | |
115 | *recv_len = hdr_recv_len; | |
03dcd410 | 116 | res = dm_i2c_read(dev, 0, recvbuf, *recv_len); |
b3f40703 | 117 | } |
c01939c7 DE |
118 | } |
119 | if (res) { | |
4a08dba0 | 120 | printf("i2c_read returned %d (rlen=%zu)\n", res, *recv_len); |
c01939c7 DE |
121 | #ifdef DEBUG |
122 | print_buffer(0, recvbuf, 1, *recv_len, 0); | |
123 | #endif | |
124 | } | |
125 | ||
126 | #ifdef DEBUG | |
127 | if (!res) { | |
128 | printf("read from TPM (%d bytes):\n", *recv_len); | |
129 | print_buffer(0, recvbuf, 1, *recv_len, 0); | |
130 | } | |
131 | #endif | |
132 | ||
133 | return res; | |
134 | } | |
302c5dba CR |
135 | |
136 | static int tpm_atmel_twi_probe(struct udevice *dev) | |
137 | { | |
fdb4a5fc | 138 | i2c_set_chip_offset_len(dev, 0); |
302c5dba CR |
139 | return 0; |
140 | } | |
141 | ||
142 | static const struct udevice_id tpm_atmel_twi_ids[] = { | |
143 | { .compatible = "atmel,at97sc3204t"}, | |
144 | { } | |
145 | }; | |
146 | ||
147 | static const struct tpm_ops tpm_atmel_twi_ops = { | |
148 | .open = tpm_atmel_twi_open, | |
149 | .close = tpm_atmel_twi_close, | |
150 | .xfer = tpm_atmel_twi_xfer, | |
151 | .get_desc = tpm_atmel_twi_get_desc, | |
152 | }; | |
153 | ||
154 | U_BOOT_DRIVER(tpm_atmel_twi) = { | |
155 | .name = "tpm_atmel_twi", | |
156 | .id = UCLASS_TPM, | |
157 | .of_match = tpm_atmel_twi_ids, | |
158 | .ops = &tpm_atmel_twi_ops, | |
159 | .probe = tpm_atmel_twi_probe, | |
160 | }; |