190899cc0SChe-liang Chiou /*
290899cc0SChe-liang Chiou * Copyright (c) 2011 The Chromium OS Authors.
390899cc0SChe-liang Chiou *
41a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+
590899cc0SChe-liang Chiou */
690899cc0SChe-liang Chiou
790899cc0SChe-liang Chiou /*
890899cc0SChe-liang Chiou * The code in this file is based on the article "Writing a TPM Device Driver"
990899cc0SChe-liang Chiou * published on http://ptgmedia.pearsoncmg.com.
1090899cc0SChe-liang Chiou *
1190899cc0SChe-liang Chiou * One principal difference is that in the simplest config the other than 0
1290899cc0SChe-liang Chiou * TPM localities do not get mapped by some devices (for instance, by Infineon
1390899cc0SChe-liang Chiou * slb9635), so this driver provides access to locality 0 only.
1490899cc0SChe-liang Chiou */
1590899cc0SChe-liang Chiou
1690899cc0SChe-liang Chiou #include <common.h>
17d616ba5fSSimon Glass #include <dm.h>
18d616ba5fSSimon Glass #include <mapmem.h>
1990899cc0SChe-liang Chiou #include <tpm.h>
20d616ba5fSSimon Glass #include <asm/io.h>
2190899cc0SChe-liang Chiou
2290899cc0SChe-liang Chiou #define PREFIX "lpc_tpm: "
2390899cc0SChe-liang Chiou
24a982b6f5SGeorge McCollister enum i2c_chip_type {
25a982b6f5SGeorge McCollister SLB9635,
26a982b6f5SGeorge McCollister AT97SC3204,
27a982b6f5SGeorge McCollister };
28a982b6f5SGeorge McCollister
29a982b6f5SGeorge McCollister static const char * const chip_name[] = {
30a982b6f5SGeorge McCollister [SLB9635] = "Infineon SLB9635 TT 1.2",
31a982b6f5SGeorge McCollister [AT97SC3204] = "Atmel AT97SC3204",
32a982b6f5SGeorge McCollister };
33a982b6f5SGeorge McCollister
34a982b6f5SGeorge McCollister static const u32 chip_didvid[] = {
35a982b6f5SGeorge McCollister [SLB9635] = 0xb15d1,
36a982b6f5SGeorge McCollister [AT97SC3204] = 0x32041114,
37a982b6f5SGeorge McCollister };
38a982b6f5SGeorge McCollister
3990899cc0SChe-liang Chiou struct tpm_locality {
4090899cc0SChe-liang Chiou u32 access;
4190899cc0SChe-liang Chiou u8 padding0[4];
4290899cc0SChe-liang Chiou u32 int_enable;
4390899cc0SChe-liang Chiou u8 vector;
4490899cc0SChe-liang Chiou u8 padding1[3];
4590899cc0SChe-liang Chiou u32 int_status;
4690899cc0SChe-liang Chiou u32 int_capability;
4790899cc0SChe-liang Chiou u32 tpm_status;
4890899cc0SChe-liang Chiou u8 padding2[8];
4990899cc0SChe-liang Chiou u8 data;
5090899cc0SChe-liang Chiou u8 padding3[3803];
5190899cc0SChe-liang Chiou u32 did_vid;
5290899cc0SChe-liang Chiou u8 rid;
5390899cc0SChe-liang Chiou u8 padding4[251];
5490899cc0SChe-liang Chiou };
5590899cc0SChe-liang Chiou
56d616ba5fSSimon Glass struct tpm_tis_lpc_priv {
57d616ba5fSSimon Glass struct tpm_locality *regs;
58d616ba5fSSimon Glass };
59d616ba5fSSimon Glass
6090899cc0SChe-liang Chiou /*
6190899cc0SChe-liang Chiou * This pointer refers to the TPM chip, 5 of its localities are mapped as an
6290899cc0SChe-liang Chiou * array.
6390899cc0SChe-liang Chiou */
6490899cc0SChe-liang Chiou #define TPM_TOTAL_LOCALITIES 5
6590899cc0SChe-liang Chiou
6690899cc0SChe-liang Chiou /* Some registers' bit field definitions */
6790899cc0SChe-liang Chiou #define TIS_STS_VALID (1 << 7) /* 0x80 */
6890899cc0SChe-liang Chiou #define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */
6990899cc0SChe-liang Chiou #define TIS_STS_TPM_GO (1 << 5) /* 0x20 */
7090899cc0SChe-liang Chiou #define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */
7190899cc0SChe-liang Chiou #define TIS_STS_EXPECT (1 << 3) /* 0x08 */
7290899cc0SChe-liang Chiou #define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */
7390899cc0SChe-liang Chiou
7490899cc0SChe-liang Chiou #define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */
7590899cc0SChe-liang Chiou #define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */
7690899cc0SChe-liang Chiou #define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */
7790899cc0SChe-liang Chiou #define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */
7890899cc0SChe-liang Chiou #define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */
7990899cc0SChe-liang Chiou #define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */
8090899cc0SChe-liang Chiou #define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */
8190899cc0SChe-liang Chiou
8290899cc0SChe-liang Chiou #define TIS_STS_BURST_COUNT_MASK (0xffff)
8390899cc0SChe-liang Chiou #define TIS_STS_BURST_COUNT_SHIFT (8)
8490899cc0SChe-liang Chiou
8590899cc0SChe-liang Chiou /* 1 second is plenty for anything TPM does. */
8690899cc0SChe-liang Chiou #define MAX_DELAY_US (1000 * 1000)
8790899cc0SChe-liang Chiou
8890899cc0SChe-liang Chiou /* Retrieve burst count value out of the status register contents. */
burst_count(u32 status)8990899cc0SChe-liang Chiou static u16 burst_count(u32 status)
9090899cc0SChe-liang Chiou {
91d616ba5fSSimon Glass return (status >> TIS_STS_BURST_COUNT_SHIFT) &
92d616ba5fSSimon Glass TIS_STS_BURST_COUNT_MASK;
9390899cc0SChe-liang Chiou }
9490899cc0SChe-liang Chiou
9590899cc0SChe-liang Chiou /* TPM access wrappers to support tracing */
tpm_read_byte(struct tpm_tis_lpc_priv * priv,const u8 * ptr)96d616ba5fSSimon Glass static u8 tpm_read_byte(struct tpm_tis_lpc_priv *priv, const u8 *ptr)
9790899cc0SChe-liang Chiou {
9890899cc0SChe-liang Chiou u8 ret = readb(ptr);
9990899cc0SChe-liang Chiou debug(PREFIX "Read reg 0x%4.4x returns 0x%2.2x\n",
100d616ba5fSSimon Glass (u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, ret);
10190899cc0SChe-liang Chiou return ret;
10290899cc0SChe-liang Chiou }
10390899cc0SChe-liang Chiou
tpm_read_word(struct tpm_tis_lpc_priv * priv,const u32 * ptr)104d616ba5fSSimon Glass static u32 tpm_read_word(struct tpm_tis_lpc_priv *priv, const u32 *ptr)
10590899cc0SChe-liang Chiou {
10690899cc0SChe-liang Chiou u32 ret = readl(ptr);
10790899cc0SChe-liang Chiou debug(PREFIX "Read reg 0x%4.4x returns 0x%8.8x\n",
108d616ba5fSSimon Glass (u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, ret);
10990899cc0SChe-liang Chiou return ret;
11090899cc0SChe-liang Chiou }
11190899cc0SChe-liang Chiou
tpm_write_byte(struct tpm_tis_lpc_priv * priv,u8 value,u8 * ptr)112d616ba5fSSimon Glass static void tpm_write_byte(struct tpm_tis_lpc_priv *priv, u8 value, u8 *ptr)
11390899cc0SChe-liang Chiou {
11490899cc0SChe-liang Chiou debug(PREFIX "Write reg 0x%4.4x with 0x%2.2x\n",
115d616ba5fSSimon Glass (u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, value);
11690899cc0SChe-liang Chiou writeb(value, ptr);
11790899cc0SChe-liang Chiou }
11890899cc0SChe-liang Chiou
tpm_write_word(struct tpm_tis_lpc_priv * priv,u32 value,u32 * ptr)119d616ba5fSSimon Glass static void tpm_write_word(struct tpm_tis_lpc_priv *priv, u32 value,
120d616ba5fSSimon Glass u32 *ptr)
12190899cc0SChe-liang Chiou {
12290899cc0SChe-liang Chiou debug(PREFIX "Write reg 0x%4.4x with 0x%8.8x\n",
123d616ba5fSSimon Glass (u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, value);
12490899cc0SChe-liang Chiou writel(value, ptr);
12590899cc0SChe-liang Chiou }
12690899cc0SChe-liang Chiou
12790899cc0SChe-liang Chiou /*
12890899cc0SChe-liang Chiou * tis_wait_reg()
12990899cc0SChe-liang Chiou *
13090899cc0SChe-liang Chiou * Wait for at least a second for a register to change its state to match the
13190899cc0SChe-liang Chiou * expected state. Normally the transition happens within microseconds.
13290899cc0SChe-liang Chiou *
13390899cc0SChe-liang Chiou * @reg - pointer to the TPM register
13490899cc0SChe-liang Chiou * @mask - bitmask for the bitfield(s) to watch
13590899cc0SChe-liang Chiou * @expected - value the field(s) are supposed to be set to
13690899cc0SChe-liang Chiou *
13790899cc0SChe-liang Chiou * Returns the register contents in case the expected value was found in the
138d616ba5fSSimon Glass * appropriate register bits, or -ETIMEDOUT on timeout.
13990899cc0SChe-liang Chiou */
tis_wait_reg(struct tpm_tis_lpc_priv * priv,u32 * reg,u8 mask,u8 expected)140d616ba5fSSimon Glass static int tis_wait_reg(struct tpm_tis_lpc_priv *priv, u32 *reg, u8 mask,
141d616ba5fSSimon Glass u8 expected)
14290899cc0SChe-liang Chiou {
14390899cc0SChe-liang Chiou u32 time_us = MAX_DELAY_US;
14490899cc0SChe-liang Chiou
14590899cc0SChe-liang Chiou while (time_us > 0) {
146d616ba5fSSimon Glass u32 value = tpm_read_word(priv, reg);
14790899cc0SChe-liang Chiou if ((value & mask) == expected)
14890899cc0SChe-liang Chiou return value;
14990899cc0SChe-liang Chiou udelay(1); /* 1 us */
15090899cc0SChe-liang Chiou time_us--;
15190899cc0SChe-liang Chiou }
152d616ba5fSSimon Glass
153d616ba5fSSimon Glass return -ETIMEDOUT;
15490899cc0SChe-liang Chiou }
15590899cc0SChe-liang Chiou
15690899cc0SChe-liang Chiou /*
15790899cc0SChe-liang Chiou * Probe the TPM device and try determining its manufacturer/device name.
15890899cc0SChe-liang Chiou *
159d616ba5fSSimon Glass * Returns 0 on success, -ve on error
16090899cc0SChe-liang Chiou */
tpm_tis_lpc_probe(struct udevice * dev)161d616ba5fSSimon Glass static int tpm_tis_lpc_probe(struct udevice *dev)
16290899cc0SChe-liang Chiou {
163d616ba5fSSimon Glass struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
164d616ba5fSSimon Glass fdt_addr_t addr;
165d616ba5fSSimon Glass u32 didvid;
166a982b6f5SGeorge McCollister ulong chip_type = dev_get_driver_data(dev);
16790899cc0SChe-liang Chiou
168*a821c4afSSimon Glass addr = devfdt_get_addr(dev);
169d616ba5fSSimon Glass if (addr == FDT_ADDR_T_NONE)
170d616ba5fSSimon Glass return -EINVAL;
171d616ba5fSSimon Glass priv->regs = map_sysmem(addr, 0);
172d616ba5fSSimon Glass didvid = tpm_read_word(priv, &priv->regs[0].did_vid);
17390899cc0SChe-liang Chiou
174a982b6f5SGeorge McCollister if (didvid != chip_didvid[chip_type]) {
175a982b6f5SGeorge McCollister u32 vid, did;
17690899cc0SChe-liang Chiou vid = didvid & 0xffff;
17790899cc0SChe-liang Chiou did = (didvid >> 16) & 0xffff;
178d616ba5fSSimon Glass debug("Invalid vendor/device ID %04x/%04x\n", vid, did);
179a982b6f5SGeorge McCollister return -ENODEV;
18090899cc0SChe-liang Chiou }
18190899cc0SChe-liang Chiou
182a982b6f5SGeorge McCollister debug("Found TPM: %s\n", chip_name[chip_type]);
183d616ba5fSSimon Glass
18490899cc0SChe-liang Chiou return 0;
18590899cc0SChe-liang Chiou }
18690899cc0SChe-liang Chiou
18790899cc0SChe-liang Chiou /*
18890899cc0SChe-liang Chiou * tis_senddata()
18990899cc0SChe-liang Chiou *
19090899cc0SChe-liang Chiou * send the passed in data to the TPM device.
19190899cc0SChe-liang Chiou *
19290899cc0SChe-liang Chiou * @data - address of the data to send, byte by byte
19390899cc0SChe-liang Chiou * @len - length of the data to send
19490899cc0SChe-liang Chiou *
195d616ba5fSSimon Glass * Returns 0 on success, -ve on error (in case the device does not accept
196d616ba5fSSimon Glass * the entire command).
19790899cc0SChe-liang Chiou */
tis_senddata(struct udevice * dev,const u8 * data,size_t len)198d616ba5fSSimon Glass static int tis_senddata(struct udevice *dev, const u8 *data, size_t len)
19990899cc0SChe-liang Chiou {
200d616ba5fSSimon Glass struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
201d616ba5fSSimon Glass struct tpm_locality *regs = priv->regs;
20290899cc0SChe-liang Chiou u32 offset = 0;
20390899cc0SChe-liang Chiou u16 burst = 0;
20490899cc0SChe-liang Chiou u32 max_cycles = 0;
20590899cc0SChe-liang Chiou u8 locality = 0;
20690899cc0SChe-liang Chiou u32 value;
20790899cc0SChe-liang Chiou
208d616ba5fSSimon Glass value = tis_wait_reg(priv, ®s[locality].tpm_status,
20990899cc0SChe-liang Chiou TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY);
210d616ba5fSSimon Glass if (value == -ETIMEDOUT) {
21190899cc0SChe-liang Chiou printf("%s:%d - failed to get 'command_ready' status\n",
21290899cc0SChe-liang Chiou __FILE__, __LINE__);
213d616ba5fSSimon Glass return value;
21490899cc0SChe-liang Chiou }
21590899cc0SChe-liang Chiou burst = burst_count(value);
21690899cc0SChe-liang Chiou
21790899cc0SChe-liang Chiou while (1) {
21890899cc0SChe-liang Chiou unsigned count;
21990899cc0SChe-liang Chiou
22090899cc0SChe-liang Chiou /* Wait till the device is ready to accept more data. */
22190899cc0SChe-liang Chiou while (!burst) {
22290899cc0SChe-liang Chiou if (max_cycles++ == MAX_DELAY_US) {
22322230e91SSimon Glass printf("%s:%d failed to feed %zd bytes of %zd\n",
22490899cc0SChe-liang Chiou __FILE__, __LINE__, len - offset, len);
225d616ba5fSSimon Glass return -ETIMEDOUT;
22690899cc0SChe-liang Chiou }
22790899cc0SChe-liang Chiou udelay(1);
228d616ba5fSSimon Glass burst = burst_count(tpm_read_word(priv,
229d616ba5fSSimon Glass ®s[locality].tpm_status));
23090899cc0SChe-liang Chiou }
23190899cc0SChe-liang Chiou
23290899cc0SChe-liang Chiou max_cycles = 0;
23390899cc0SChe-liang Chiou
23490899cc0SChe-liang Chiou /*
23590899cc0SChe-liang Chiou * Calculate number of bytes the TPM is ready to accept in one
23690899cc0SChe-liang Chiou * shot.
23790899cc0SChe-liang Chiou *
23890899cc0SChe-liang Chiou * We want to send the last byte outside of the loop (hence
23990899cc0SChe-liang Chiou * the -1 below) to make sure that the 'expected' status bit
24090899cc0SChe-liang Chiou * changes to zero exactly after the last byte is fed into the
24190899cc0SChe-liang Chiou * FIFO.
24290899cc0SChe-liang Chiou */
24322230e91SSimon Glass count = min((size_t)burst, len - offset - 1);
24490899cc0SChe-liang Chiou while (count--)
245d616ba5fSSimon Glass tpm_write_byte(priv, data[offset++],
246d616ba5fSSimon Glass ®s[locality].data);
24790899cc0SChe-liang Chiou
248d616ba5fSSimon Glass value = tis_wait_reg(priv, ®s[locality].tpm_status,
24990899cc0SChe-liang Chiou TIS_STS_VALID, TIS_STS_VALID);
25090899cc0SChe-liang Chiou
251d616ba5fSSimon Glass if ((value == -ETIMEDOUT) || !(value & TIS_STS_EXPECT)) {
25290899cc0SChe-liang Chiou printf("%s:%d TPM command feed overflow\n",
25390899cc0SChe-liang Chiou __FILE__, __LINE__);
254d616ba5fSSimon Glass return value == -ETIMEDOUT ? value : -EIO;
25590899cc0SChe-liang Chiou }
25690899cc0SChe-liang Chiou
25790899cc0SChe-liang Chiou burst = burst_count(value);
25890899cc0SChe-liang Chiou if ((offset == (len - 1)) && burst) {
25990899cc0SChe-liang Chiou /*
26090899cc0SChe-liang Chiou * We need to be able to send the last byte to the
26190899cc0SChe-liang Chiou * device, so burst size must be nonzero before we
26290899cc0SChe-liang Chiou * break out.
26390899cc0SChe-liang Chiou */
26490899cc0SChe-liang Chiou break;
26590899cc0SChe-liang Chiou }
26690899cc0SChe-liang Chiou }
26790899cc0SChe-liang Chiou
26890899cc0SChe-liang Chiou /* Send the last byte. */
269d616ba5fSSimon Glass tpm_write_byte(priv, data[offset++], ®s[locality].data);
27090899cc0SChe-liang Chiou /*
27190899cc0SChe-liang Chiou * Verify that TPM does not expect any more data as part of this
27290899cc0SChe-liang Chiou * command.
27390899cc0SChe-liang Chiou */
274d616ba5fSSimon Glass value = tis_wait_reg(priv, ®s[locality].tpm_status,
27590899cc0SChe-liang Chiou TIS_STS_VALID, TIS_STS_VALID);
276d616ba5fSSimon Glass if ((value == -ETIMEDOUT) || (value & TIS_STS_EXPECT)) {
27790899cc0SChe-liang Chiou printf("%s:%d unexpected TPM status 0x%x\n",
27890899cc0SChe-liang Chiou __FILE__, __LINE__, value);
279d616ba5fSSimon Glass return value == -ETIMEDOUT ? value : -EIO;
28090899cc0SChe-liang Chiou }
28190899cc0SChe-liang Chiou
28290899cc0SChe-liang Chiou /* OK, sitting pretty, let's start the command execution. */
283d616ba5fSSimon Glass tpm_write_word(priv, TIS_STS_TPM_GO, ®s[locality].tpm_status);
28490899cc0SChe-liang Chiou return 0;
28590899cc0SChe-liang Chiou }
28690899cc0SChe-liang Chiou
28790899cc0SChe-liang Chiou /*
28890899cc0SChe-liang Chiou * tis_readresponse()
28990899cc0SChe-liang Chiou *
29090899cc0SChe-liang Chiou * read the TPM device response after a command was issued.
29190899cc0SChe-liang Chiou *
29290899cc0SChe-liang Chiou * @buffer - address where to read the response, byte by byte.
29390899cc0SChe-liang Chiou * @len - pointer to the size of buffer
29490899cc0SChe-liang Chiou *
29590899cc0SChe-liang Chiou * On success stores the number of received bytes to len and returns 0. On
29690899cc0SChe-liang Chiou * errors (misformatted TPM data or synchronization problems) returns
297d616ba5fSSimon Glass * -ve value.
29890899cc0SChe-liang Chiou */
tis_readresponse(struct udevice * dev,u8 * buffer,size_t len)299d616ba5fSSimon Glass static int tis_readresponse(struct udevice *dev, u8 *buffer, size_t len)
30090899cc0SChe-liang Chiou {
301d616ba5fSSimon Glass struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
302d616ba5fSSimon Glass struct tpm_locality *regs = priv->regs;
30390899cc0SChe-liang Chiou u16 burst;
30490899cc0SChe-liang Chiou u32 value;
30590899cc0SChe-liang Chiou u32 offset = 0;
30690899cc0SChe-liang Chiou u8 locality = 0;
30790899cc0SChe-liang Chiou const u32 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
308d616ba5fSSimon Glass u32 expected_count = len;
30990899cc0SChe-liang Chiou int max_cycles = 0;
31090899cc0SChe-liang Chiou
31190899cc0SChe-liang Chiou /* Wait for the TPM to process the command. */
312d616ba5fSSimon Glass value = tis_wait_reg(priv, ®s[locality].tpm_status,
31390899cc0SChe-liang Chiou has_data, has_data);
314d616ba5fSSimon Glass if (value == -ETIMEDOUT) {
31590899cc0SChe-liang Chiou printf("%s:%d failed processing command\n",
31690899cc0SChe-liang Chiou __FILE__, __LINE__);
317d616ba5fSSimon Glass return value;
31890899cc0SChe-liang Chiou }
31990899cc0SChe-liang Chiou
32090899cc0SChe-liang Chiou do {
32190899cc0SChe-liang Chiou while ((burst = burst_count(value)) == 0) {
32290899cc0SChe-liang Chiou if (max_cycles++ == MAX_DELAY_US) {
32390899cc0SChe-liang Chiou printf("%s:%d TPM stuck on read\n",
32490899cc0SChe-liang Chiou __FILE__, __LINE__);
325d616ba5fSSimon Glass return -EIO;
32690899cc0SChe-liang Chiou }
32790899cc0SChe-liang Chiou udelay(1);
328d616ba5fSSimon Glass value = tpm_read_word(priv, ®s[locality].tpm_status);
32990899cc0SChe-liang Chiou }
33090899cc0SChe-liang Chiou
33190899cc0SChe-liang Chiou max_cycles = 0;
33290899cc0SChe-liang Chiou
33390899cc0SChe-liang Chiou while (burst-- && (offset < expected_count)) {
334d616ba5fSSimon Glass buffer[offset++] = tpm_read_byte(priv,
335d616ba5fSSimon Glass ®s[locality].data);
33690899cc0SChe-liang Chiou
33790899cc0SChe-liang Chiou if (offset == 6) {
33890899cc0SChe-liang Chiou /*
33990899cc0SChe-liang Chiou * We got the first six bytes of the reply,
34090899cc0SChe-liang Chiou * let's figure out how many bytes to expect
34190899cc0SChe-liang Chiou * total - it is stored as a 4 byte number in
34290899cc0SChe-liang Chiou * network order, starting with offset 2 into
34390899cc0SChe-liang Chiou * the body of the reply.
34490899cc0SChe-liang Chiou */
34590899cc0SChe-liang Chiou u32 real_length;
34690899cc0SChe-liang Chiou memcpy(&real_length,
34790899cc0SChe-liang Chiou buffer + 2,
34890899cc0SChe-liang Chiou sizeof(real_length));
34990899cc0SChe-liang Chiou expected_count = be32_to_cpu(real_length);
35090899cc0SChe-liang Chiou
35190899cc0SChe-liang Chiou if ((expected_count < offset) ||
352d616ba5fSSimon Glass (expected_count > len)) {
35390899cc0SChe-liang Chiou printf("%s:%d bad response size %d\n",
35490899cc0SChe-liang Chiou __FILE__, __LINE__,
35590899cc0SChe-liang Chiou expected_count);
356d616ba5fSSimon Glass return -ENOSPC;
35790899cc0SChe-liang Chiou }
35890899cc0SChe-liang Chiou }
35990899cc0SChe-liang Chiou }
36090899cc0SChe-liang Chiou
36190899cc0SChe-liang Chiou /* Wait for the next portion. */
362d616ba5fSSimon Glass value = tis_wait_reg(priv, ®s[locality].tpm_status,
36390899cc0SChe-liang Chiou TIS_STS_VALID, TIS_STS_VALID);
364d616ba5fSSimon Glass if (value == -ETIMEDOUT) {
36590899cc0SChe-liang Chiou printf("%s:%d failed to read response\n",
36690899cc0SChe-liang Chiou __FILE__, __LINE__);
367d616ba5fSSimon Glass return value;
36890899cc0SChe-liang Chiou }
36990899cc0SChe-liang Chiou
37090899cc0SChe-liang Chiou if (offset == expected_count)
37190899cc0SChe-liang Chiou break; /* We got all we needed. */
37290899cc0SChe-liang Chiou
37390899cc0SChe-liang Chiou } while ((value & has_data) == has_data);
37490899cc0SChe-liang Chiou
37590899cc0SChe-liang Chiou /*
37690899cc0SChe-liang Chiou * Make sure we indeed read all there was. The TIS_STS_VALID bit is
37790899cc0SChe-liang Chiou * known to be set.
37890899cc0SChe-liang Chiou */
37990899cc0SChe-liang Chiou if (value & TIS_STS_DATA_AVAILABLE) {
38090899cc0SChe-liang Chiou printf("%s:%d wrong receive status %x\n",
38190899cc0SChe-liang Chiou __FILE__, __LINE__, value);
382d616ba5fSSimon Glass return -EBADMSG;
38390899cc0SChe-liang Chiou }
38490899cc0SChe-liang Chiou
38590899cc0SChe-liang Chiou /* Tell the TPM that we are done. */
386d616ba5fSSimon Glass tpm_write_word(priv, TIS_STS_COMMAND_READY,
387d616ba5fSSimon Glass ®s[locality].tpm_status);
388d616ba5fSSimon Glass
389d616ba5fSSimon Glass return offset;
39090899cc0SChe-liang Chiou }
39190899cc0SChe-liang Chiou
tpm_tis_lpc_open(struct udevice * dev)392d616ba5fSSimon Glass static int tpm_tis_lpc_open(struct udevice *dev)
39390899cc0SChe-liang Chiou {
394d616ba5fSSimon Glass struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
395d616ba5fSSimon Glass struct tpm_locality *regs = priv->regs;
39690899cc0SChe-liang Chiou u8 locality = 0; /* we use locality zero for everything. */
397d616ba5fSSimon Glass int ret;
39890899cc0SChe-liang Chiou
39990899cc0SChe-liang Chiou /* now request access to locality. */
400d616ba5fSSimon Glass tpm_write_word(priv, TIS_ACCESS_REQUEST_USE, ®s[locality].access);
40190899cc0SChe-liang Chiou
40290899cc0SChe-liang Chiou /* did we get a lock? */
403d616ba5fSSimon Glass ret = tis_wait_reg(priv, ®s[locality].access,
40490899cc0SChe-liang Chiou TIS_ACCESS_ACTIVE_LOCALITY,
405d616ba5fSSimon Glass TIS_ACCESS_ACTIVE_LOCALITY);
406d616ba5fSSimon Glass if (ret == -ETIMEDOUT) {
40790899cc0SChe-liang Chiou printf("%s:%d - failed to lock locality %d\n",
40890899cc0SChe-liang Chiou __FILE__, __LINE__, locality);
409d616ba5fSSimon Glass return ret;
41090899cc0SChe-liang Chiou }
41190899cc0SChe-liang Chiou
412d616ba5fSSimon Glass tpm_write_word(priv, TIS_STS_COMMAND_READY,
413d616ba5fSSimon Glass ®s[locality].tpm_status);
41490899cc0SChe-liang Chiou return 0;
41590899cc0SChe-liang Chiou }
41690899cc0SChe-liang Chiou
tpm_tis_lpc_close(struct udevice * dev)417d616ba5fSSimon Glass static int tpm_tis_lpc_close(struct udevice *dev)
41890899cc0SChe-liang Chiou {
419d616ba5fSSimon Glass struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
420d616ba5fSSimon Glass struct tpm_locality *regs = priv->regs;
42190899cc0SChe-liang Chiou u8 locality = 0;
42290899cc0SChe-liang Chiou
423d616ba5fSSimon Glass if (tpm_read_word(priv, ®s[locality].access) &
42490899cc0SChe-liang Chiou TIS_ACCESS_ACTIVE_LOCALITY) {
425d616ba5fSSimon Glass tpm_write_word(priv, TIS_ACCESS_ACTIVE_LOCALITY,
426d616ba5fSSimon Glass ®s[locality].access);
42790899cc0SChe-liang Chiou
428d616ba5fSSimon Glass if (tis_wait_reg(priv, ®s[locality].access,
429d616ba5fSSimon Glass TIS_ACCESS_ACTIVE_LOCALITY, 0) == -ETIMEDOUT) {
43090899cc0SChe-liang Chiou printf("%s:%d - failed to release locality %d\n",
43190899cc0SChe-liang Chiou __FILE__, __LINE__, locality);
432d616ba5fSSimon Glass return -ETIMEDOUT;
43390899cc0SChe-liang Chiou }
43490899cc0SChe-liang Chiou }
43590899cc0SChe-liang Chiou return 0;
43690899cc0SChe-liang Chiou }
43790899cc0SChe-liang Chiou
tpm_tis_get_desc(struct udevice * dev,char * buf,int size)438d616ba5fSSimon Glass static int tpm_tis_get_desc(struct udevice *dev, char *buf, int size)
43990899cc0SChe-liang Chiou {
440a982b6f5SGeorge McCollister ulong chip_type = dev_get_driver_data(dev);
441a982b6f5SGeorge McCollister
442d616ba5fSSimon Glass if (size < 50)
443d616ba5fSSimon Glass return -ENOSPC;
444d616ba5fSSimon Glass
445a982b6f5SGeorge McCollister return snprintf(buf, size, "1.2 TPM (%s)",
446a982b6f5SGeorge McCollister chip_name[chip_type]);
44790899cc0SChe-liang Chiou }
44890899cc0SChe-liang Chiou
449d616ba5fSSimon Glass
450d616ba5fSSimon Glass static const struct tpm_ops tpm_tis_lpc_ops = {
451d616ba5fSSimon Glass .open = tpm_tis_lpc_open,
452d616ba5fSSimon Glass .close = tpm_tis_lpc_close,
453d616ba5fSSimon Glass .get_desc = tpm_tis_get_desc,
454d616ba5fSSimon Glass .send = tis_senddata,
455d616ba5fSSimon Glass .recv = tis_readresponse,
456d616ba5fSSimon Glass };
457d616ba5fSSimon Glass
458d616ba5fSSimon Glass static const struct udevice_id tpm_tis_lpc_ids[] = {
459a982b6f5SGeorge McCollister { .compatible = "infineon,slb9635lpc", .data = SLB9635 },
460a982b6f5SGeorge McCollister { .compatible = "atmel,at97sc3204", .data = AT97SC3204 },
461d616ba5fSSimon Glass { }
462d616ba5fSSimon Glass };
463d616ba5fSSimon Glass
464d616ba5fSSimon Glass U_BOOT_DRIVER(tpm_tis_lpc) = {
465d616ba5fSSimon Glass .name = "tpm_tis_lpc",
466d616ba5fSSimon Glass .id = UCLASS_TPM,
467d616ba5fSSimon Glass .of_match = tpm_tis_lpc_ids,
468d616ba5fSSimon Glass .ops = &tpm_tis_lpc_ops,
469d616ba5fSSimon Glass .probe = tpm_tis_lpc_probe,
470d616ba5fSSimon Glass .priv_auto_alloc_size = sizeof(struct tpm_tis_lpc_priv),
471d616ba5fSSimon Glass };
472