1c01939c7SDirk Eibach /*
207470d6fSSimon Glass * Copyright (C) 2013 Guntermann & Drunck, GmbH
3c01939c7SDirk Eibach *
407470d6fSSimon Glass * Written by Dirk Eibach <eibach@gdsys.de>
5c01939c7SDirk Eibach *
607470d6fSSimon Glass * SPDX-License-Identifier: GPL-2.0+
7c01939c7SDirk Eibach */
8c01939c7SDirk Eibach
9c01939c7SDirk Eibach #include <common.h>
10302c5dbaSChristophe Ricard #include <dm.h>
11c01939c7SDirk Eibach #include <tpm.h>
12c01939c7SDirk Eibach #include <i2c.h>
13c01939c7SDirk Eibach #include <asm/unaligned.h>
14c01939c7SDirk Eibach
15302c5dbaSChristophe Ricard #include "tpm_internal.h"
16302c5dbaSChristophe Ricard
17c01939c7SDirk Eibach #define ATMEL_TPM_TIMEOUT_MS 5000 /* sufficient for anything but
18c01939c7SDirk Eibach generating/exporting keys */
19c01939c7SDirk Eibach
20c01939c7SDirk Eibach /*
21302c5dbaSChristophe Ricard * tpm_atmel_twi_open()
22c01939c7SDirk Eibach *
23c01939c7SDirk Eibach * Requests access to locality 0 for the caller. After all commands have been
24c01939c7SDirk Eibach * completed the caller is supposed to call tis_close().
25c01939c7SDirk Eibach *
26c01939c7SDirk Eibach * Returns 0 on success, -1 on failure.
27c01939c7SDirk Eibach */
tpm_atmel_twi_open(struct udevice * dev)28302c5dbaSChristophe Ricard static int tpm_atmel_twi_open(struct udevice *dev)
29c01939c7SDirk Eibach {
30c01939c7SDirk Eibach return 0;
31c01939c7SDirk Eibach }
32c01939c7SDirk Eibach
33c01939c7SDirk Eibach /*
34302c5dbaSChristophe Ricard * tpm_atmel_twi_close()
35c01939c7SDirk Eibach *
36c01939c7SDirk Eibach * terminate the currect session with the TPM by releasing the locked
37c01939c7SDirk Eibach * locality. Returns 0 on success of -1 on failure (in case lock
38c01939c7SDirk Eibach * removal did not succeed).
39c01939c7SDirk Eibach */
tpm_atmel_twi_close(struct udevice * dev)40302c5dbaSChristophe Ricard static int tpm_atmel_twi_close(struct udevice *dev)
41c01939c7SDirk Eibach {
42c01939c7SDirk Eibach return 0;
43c01939c7SDirk Eibach }
44c01939c7SDirk Eibach
45c01939c7SDirk Eibach /*
46302c5dbaSChristophe Ricard * tpm_atmel_twi_get_desc()
47302c5dbaSChristophe Ricard *
48302c5dbaSChristophe Ricard * @dev: Device to check
49302c5dbaSChristophe Ricard * @buf: Buffer to put the string
50302c5dbaSChristophe Ricard * @size: Maximum size of buffer
51302c5dbaSChristophe Ricard * @return length of string, or -ENOSPC it no space
52302c5dbaSChristophe Ricard */
tpm_atmel_twi_get_desc(struct udevice * dev,char * buf,int size)53302c5dbaSChristophe Ricard static int tpm_atmel_twi_get_desc(struct udevice *dev, char *buf, int size)
54302c5dbaSChristophe Ricard {
55302c5dbaSChristophe Ricard return 0;
56302c5dbaSChristophe Ricard }
57302c5dbaSChristophe Ricard
58302c5dbaSChristophe Ricard /*
59302c5dbaSChristophe Ricard * tpm_atmel_twi_xfer()
60c01939c7SDirk Eibach *
61c01939c7SDirk Eibach * Send the requested data to the TPM and then try to get its response
62c01939c7SDirk Eibach *
63c01939c7SDirk Eibach * @sendbuf - buffer of the data to send
64c01939c7SDirk Eibach * @send_size size of the data to send
65c01939c7SDirk Eibach * @recvbuf - memory to save the response to
66c01939c7SDirk Eibach * @recv_len - pointer to the size of the response buffer
67c01939c7SDirk Eibach *
68c01939c7SDirk Eibach * Returns 0 on success (and places the number of response bytes at recv_len)
69c01939c7SDirk Eibach * or -1 on failure.
70c01939c7SDirk Eibach */
tpm_atmel_twi_xfer(struct udevice * dev,const uint8_t * sendbuf,size_t send_size,uint8_t * recvbuf,size_t * recv_len)71302c5dbaSChristophe Ricard static int tpm_atmel_twi_xfer(struct udevice *dev,
72302c5dbaSChristophe Ricard const uint8_t *sendbuf, size_t send_size,
73302c5dbaSChristophe Ricard uint8_t *recvbuf, size_t *recv_len)
74c01939c7SDirk Eibach {
75c01939c7SDirk Eibach int res;
76c01939c7SDirk Eibach unsigned long start;
77c01939c7SDirk Eibach
78c01939c7SDirk Eibach #ifdef DEBUG
79c01939c7SDirk Eibach memset(recvbuf, 0xcc, *recv_len);
80c01939c7SDirk Eibach printf("send to TPM (%d bytes, recv_len=%d):\n", send_size, *recv_len);
81c01939c7SDirk Eibach print_buffer(0, (void *)sendbuf, 1, send_size, 0);
82c01939c7SDirk Eibach #endif
83c01939c7SDirk Eibach
84*03dcd410Smario.six@gdsys.cc #ifndef CONFIG_DM_I2C
85c01939c7SDirk Eibach res = i2c_write(0x29, 0, 0, (uchar *)sendbuf, send_size);
86*03dcd410Smario.six@gdsys.cc #else
87*03dcd410Smario.six@gdsys.cc res = dm_i2c_write(dev, 0, sendbuf, send_size);
88*03dcd410Smario.six@gdsys.cc #endif
89c01939c7SDirk Eibach if (res) {
90c01939c7SDirk Eibach printf("i2c_write returned %d\n", res);
91c01939c7SDirk Eibach return -1;
92c01939c7SDirk Eibach }
93c01939c7SDirk Eibach
94c01939c7SDirk Eibach start = get_timer(0);
95*03dcd410Smario.six@gdsys.cc #ifndef CONFIG_DM_I2C
96*03dcd410Smario.six@gdsys.cc while ((res = i2c_read(0x29, 0, 0, recvbuf, 10)))
97*03dcd410Smario.six@gdsys.cc #else
98*03dcd410Smario.six@gdsys.cc while ((res = dm_i2c_read(dev, 0, recvbuf, 10)))
99*03dcd410Smario.six@gdsys.cc #endif
100*03dcd410Smario.six@gdsys.cc {
101302c5dbaSChristophe Ricard /* TODO Use TIS_TIMEOUT from tpm_tis_infineon.h */
102c01939c7SDirk Eibach if (get_timer(start) > ATMEL_TPM_TIMEOUT_MS) {
103c01939c7SDirk Eibach puts("tpm timed out\n");
104c01939c7SDirk Eibach return -1;
105c01939c7SDirk Eibach }
106c01939c7SDirk Eibach udelay(100);
107c01939c7SDirk Eibach }
108c01939c7SDirk Eibach if (!res) {
109c01939c7SDirk Eibach *recv_len = get_unaligned_be32(recvbuf + 2);
110c01939c7SDirk Eibach if (*recv_len > 10)
111*03dcd410Smario.six@gdsys.cc #ifndef CONFIG_DM_I2C
112c01939c7SDirk Eibach res = i2c_read(0x29, 0, 0, recvbuf, *recv_len);
113*03dcd410Smario.six@gdsys.cc #else
114*03dcd410Smario.six@gdsys.cc res = dm_i2c_read(dev, 0, recvbuf, *recv_len);
115*03dcd410Smario.six@gdsys.cc #endif
116c01939c7SDirk Eibach }
117c01939c7SDirk Eibach if (res) {
118c01939c7SDirk Eibach printf("i2c_read returned %d (rlen=%d)\n", res, *recv_len);
119c01939c7SDirk Eibach #ifdef DEBUG
120c01939c7SDirk Eibach print_buffer(0, recvbuf, 1, *recv_len, 0);
121c01939c7SDirk Eibach #endif
122c01939c7SDirk Eibach }
123c01939c7SDirk Eibach
124c01939c7SDirk Eibach #ifdef DEBUG
125c01939c7SDirk Eibach if (!res) {
126c01939c7SDirk Eibach printf("read from TPM (%d bytes):\n", *recv_len);
127c01939c7SDirk Eibach print_buffer(0, recvbuf, 1, *recv_len, 0);
128c01939c7SDirk Eibach }
129c01939c7SDirk Eibach #endif
130c01939c7SDirk Eibach
131c01939c7SDirk Eibach return res;
132c01939c7SDirk Eibach }
133302c5dbaSChristophe Ricard
tpm_atmel_twi_probe(struct udevice * dev)134302c5dbaSChristophe Ricard static int tpm_atmel_twi_probe(struct udevice *dev)
135302c5dbaSChristophe Ricard {
136302c5dbaSChristophe Ricard return 0;
137302c5dbaSChristophe Ricard }
138302c5dbaSChristophe Ricard
139302c5dbaSChristophe Ricard static const struct udevice_id tpm_atmel_twi_ids[] = {
140302c5dbaSChristophe Ricard { .compatible = "atmel,at97sc3204t"},
141302c5dbaSChristophe Ricard { }
142302c5dbaSChristophe Ricard };
143302c5dbaSChristophe Ricard
144302c5dbaSChristophe Ricard static const struct tpm_ops tpm_atmel_twi_ops = {
145302c5dbaSChristophe Ricard .open = tpm_atmel_twi_open,
146302c5dbaSChristophe Ricard .close = tpm_atmel_twi_close,
147302c5dbaSChristophe Ricard .xfer = tpm_atmel_twi_xfer,
148302c5dbaSChristophe Ricard .get_desc = tpm_atmel_twi_get_desc,
149302c5dbaSChristophe Ricard };
150302c5dbaSChristophe Ricard
151302c5dbaSChristophe Ricard U_BOOT_DRIVER(tpm_atmel_twi) = {
152302c5dbaSChristophe Ricard .name = "tpm_atmel_twi",
153302c5dbaSChristophe Ricard .id = UCLASS_TPM,
154302c5dbaSChristophe Ricard .of_match = tpm_atmel_twi_ids,
155302c5dbaSChristophe Ricard .ops = &tpm_atmel_twi_ops,
156302c5dbaSChristophe Ricard .probe = tpm_atmel_twi_probe,
157302c5dbaSChristophe Ricard };
158