blob: da443d3a09392af5de520fc4b92c0197a72fb23b [file] [log] [blame]
Angel Pons8a3453f2020-04-02 23:48:19 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Stefan Reinauer3008bbad2011-10-11 14:46:25 -07002
3/*
4 * The code in this file has been heavily based on the article "Writing a TPM
5 * Device Driver" published on http://ptgmedia.pearsoncmg.com and the
6 * submission by Stefan Berger on Qemu-devel mailing list.
7 *
8 * One principal difference is that in the simplest config the other than 0
9 * TPM localities do not get mapped by some devices (for instance, by
10 * Infineon slb9635), so this driver provides access to locality 0 only.
11 */
12
Elyes HAOUAS361a9352019-12-18 21:26:33 +010013#include <commonlib/helpers.h>
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070014#include <string.h>
15#include <delay.h>
Kyösti Mälkki13f66502019-03-03 08:01:05 +020016#include <device/mmio.h>
Furquan Shaikh76cedd22020-05-02 10:24:23 -070017#include <acpi/acpi.h>
18#include <acpi/acpigen.h>
19#include <acpi/acpi_device.h>
Naresh G Solanki80ff0382016-11-15 11:01:33 +053020#include <device/device.h>
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070021#include <console/console.h>
Philipp Deppenwiesed88fb362017-10-18 20:26:18 +020022#include <security/tpm/tis.h>
Sergii Dmytruk0a89d522022-10-29 16:57:07 +030023#include <security/tpm/tss.h>
Duncan Lauriedd281ed2014-10-30 15:20:19 -070024#include <device/pnp.h>
Patrick Rudolphd8d8be12020-09-21 09:48:53 +020025#include <drivers/tpm/tpm_ppi.h>
Werner Zeh92ab6112021-10-11 15:27:12 +020026#include <timer.h>
Duncan Lauriedd281ed2014-10-30 15:20:19 -070027#include "chip.h"
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070028
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070029#define PREFIX "lpc_tpm: "
Patrick Rudolphd8d8be12020-09-21 09:48:53 +020030
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070031/* coreboot wrapper for TPM driver (start) */
32#define TPM_DEBUG(fmt, args...) \
Julius Wernercd49cce2019-03-05 16:53:33 -080033 if (CONFIG(DEBUG_TPM)) { \
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070034 printk(BIOS_DEBUG, PREFIX); \
Elyes HAOUASa342f392018-10-17 10:56:26 +020035 printk(BIOS_DEBUG, fmt, ##args); \
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070036 }
Aaron Durbinc4220022013-07-24 16:02:14 -050037#define TPM_DEBUG_IO_READ(reg_, val_) \
Jon Murphy53fc6672023-09-26 21:05:37 -060038 TPM_DEBUG("Read reg %#x returns %#x\n", (reg_), (val_))
Aaron Durbinc4220022013-07-24 16:02:14 -050039#define TPM_DEBUG_IO_WRITE(reg_, val_) \
Jon Murphy53fc6672023-09-26 21:05:37 -060040 TPM_DEBUG("Write reg %#x with %#x\n", (reg_), (val_))
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070041#define printf(x...) printk(BIOS_ERR, x)
42
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070043/* coreboot wrapper for TPM driver (end) */
44
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070045/* the macro accepts the locality value, but only locality 0 is operational */
46#define TIS_REG(LOCALITY, REG) \
Patrick Rudolph56fdafb2020-07-01 20:07:08 +020047 (void *)(uintptr_t)(CONFIG_TPM_TIS_BASE_ADDRESS + (LOCALITY << 12) + REG)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070048
49/* hardware registers' offsets */
50#define TIS_REG_ACCESS 0x0
51#define TIS_REG_INT_ENABLE 0x8
52#define TIS_REG_INT_VECTOR 0xc
53#define TIS_REG_INT_STATUS 0x10
54#define TIS_REG_INTF_CAPABILITY 0x14
55#define TIS_REG_STS 0x18
Aaron Durbinc4220022013-07-24 16:02:14 -050056#define TIS_REG_BURST_COUNT 0x19
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070057#define TIS_REG_DATA_FIFO 0x24
Sergii Dmytruk76086992022-10-30 17:19:46 +020058#define TIS_REG_INTF_ID 0x30
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070059#define TIS_REG_DID_VID 0xf00
60#define TIS_REG_RID 0xf04
61
62/* Some registers' bit field definitions */
63#define TIS_STS_VALID (1 << 7) /* 0x80 */
64#define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */
65#define TIS_STS_TPM_GO (1 << 5) /* 0x20 */
66#define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */
67#define TIS_STS_EXPECT (1 << 3) /* 0x08 */
68#define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */
69
70#define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */
71#define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */
72#define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */
73#define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */
74#define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */
75#define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */
76#define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */
77
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070078 /* 1 second is plenty for anything TPM does.*/
Werner Zeh92ab6112021-10-11 15:27:12 +020079#define MAX_DELAY_US USECS_PER_SEC
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070080
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070081/*
82 * Structures defined below allow creating descriptions of TPM vendor/device
Sergii Dmytruk76086992022-10-30 17:19:46 +020083 * ID information for run time discovery.
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070084 */
85struct device_name {
86 u16 dev_id;
Sergii Dmytruk76086992022-10-30 17:19:46 +020087 enum tpm_family family;
Elyes HAOUASb0b0c8c2018-07-08 12:33:47 +020088 const char *const dev_name;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070089};
90
91struct vendor_name {
92 u16 vendor_id;
Elyes HAOUASb0b0c8c2018-07-08 12:33:47 +020093 const char *vendor_name;
94 const struct device_name *dev_names;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070095};
96
Stefan Reinauerc908fc72012-04-30 16:33:44 -070097static const struct device_name atmel_devices[] = {
Sergii Dmytruk76086992022-10-30 17:19:46 +020098 {0x3204, TPM_1, "AT97SC3204"},
Stefan Reinauerc908fc72012-04-30 16:33:44 -070099 {0xffff}
100};
101
Stefan Reinauerc668af72011-10-27 21:28:25 +0000102static const struct device_name infineon_devices[] = {
Sergii Dmytruk76086992022-10-30 17:19:46 +0200103 {0x000b, TPM_1, "SLB9635 TT 1.2"},
104 {0x001a, TPM_1, "SLB9660 TT 1.2"},
105 {0x001b, TPM_1, "SLB9670 TT 1.2"},
106 {0x001a, TPM_2, "SLB9665 TT 2.0"},
107 {0x001b, TPM_2, "SLB9670 TT 2.0"},
108 {0x001d, TPM_2, "SLB9672 TT 2.0"},
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700109 {0xffff}
110};
111
112static const struct device_name nuvoton_devices[] = {
Sergii Dmytruk76086992022-10-30 17:19:46 +0200113 {0x00fe, TPM_1, "NPCT420AA V2"},
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700114 {0xffff}
115};
116
117static const struct device_name stmicro_devices[] = {
Sergii Dmytruk76086992022-10-30 17:19:46 +0200118 {0x0000, TPM_1, "ST33ZP24" },
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700119 {0xffff}
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700120};
121
Tsung Ho Wu804a0432019-06-07 15:03:49 -0700122static const struct device_name swtpm_devices[] = {
Sergii Dmytruk76086992022-10-30 17:19:46 +0200123 {0x0001, TPM_1, "SwTPM 1.2" },
124 {0x0001, TPM_2, "SwTPM 2.0" },
Tsung Ho Wu804a0432019-06-07 15:03:49 -0700125 {0xffff}
126};
127
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700128static const struct vendor_name vendor_names[] = {
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700129 {0x1114, "Atmel", atmel_devices},
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700130 {0x15d1, "Infineon", infineon_devices},
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700131 {0x1050, "Nuvoton", nuvoton_devices},
Tsung Ho Wu804a0432019-06-07 15:03:49 -0700132 {0x1014, "TPM Emulator", swtpm_devices},
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700133 {0x104a, "ST Microelectronics", stmicro_devices},
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700134};
135
136/*
137 * Cached vendor/device ID pair to indicate that the device has been already
138 * discovered
139 */
Arthur Heymans0ca944b2019-11-20 19:51:06 +0100140static u32 vendor_dev_id;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700141
Aaron Durbinc4220022013-07-24 16:02:14 -0500142static inline u8 tpm_read_status(int locality)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700143{
Julius Werner2f37bd62015-02-19 14:51:15 -0800144 u8 value = read8(TIS_REG(locality, TIS_REG_STS));
Aaron Durbinc4220022013-07-24 16:02:14 -0500145 TPM_DEBUG_IO_READ(TIS_REG_STS, value);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700146 return value;
147}
148
Aaron Durbinc4220022013-07-24 16:02:14 -0500149static inline void tpm_write_status(u8 sts, int locality)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700150{
Aaron Durbinc4220022013-07-24 16:02:14 -0500151 TPM_DEBUG_IO_WRITE(TIS_REG_STS, sts);
Julius Werner2f37bd62015-02-19 14:51:15 -0800152 write8(TIS_REG(locality, TIS_REG_STS), sts);
Aaron Durbinc4220022013-07-24 16:02:14 -0500153}
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700154
Aaron Durbinc4220022013-07-24 16:02:14 -0500155static inline u8 tpm_read_data(int locality)
156{
Julius Werner2f37bd62015-02-19 14:51:15 -0800157 u8 value = read8(TIS_REG(locality, TIS_REG_DATA_FIFO));
Aaron Durbinc4220022013-07-24 16:02:14 -0500158 TPM_DEBUG_IO_READ(TIS_REG_DATA_FIFO, value);
159 return value;
160}
161
162static inline void tpm_write_data(u8 data, int locality)
163{
Werner Zeha31d6cd2021-10-07 07:15:38 +0200164 TPM_DEBUG_IO_WRITE(TIS_REG_DATA_FIFO, data);
Julius Werner2f37bd62015-02-19 14:51:15 -0800165 write8(TIS_REG(locality, TIS_REG_DATA_FIFO), data);
Aaron Durbinc4220022013-07-24 16:02:14 -0500166}
167
168static inline u16 tpm_read_burst_count(int locality)
169{
170 u16 count;
Julius Werner2f37bd62015-02-19 14:51:15 -0800171 count = read8(TIS_REG(locality, TIS_REG_BURST_COUNT));
172 count |= read8(TIS_REG(locality, TIS_REG_BURST_COUNT + 1)) << 8;
Aaron Durbinc4220022013-07-24 16:02:14 -0500173 TPM_DEBUG_IO_READ(TIS_REG_BURST_COUNT, count);
174 return count;
175}
176
177static inline u8 tpm_read_access(int locality)
178{
Julius Werner2f37bd62015-02-19 14:51:15 -0800179 u8 value = read8(TIS_REG(locality, TIS_REG_ACCESS));
Aaron Durbinc4220022013-07-24 16:02:14 -0500180 TPM_DEBUG_IO_READ(TIS_REG_ACCESS, value);
181 return value;
182}
183
184static inline void tpm_write_access(u8 data, int locality)
185{
186 TPM_DEBUG_IO_WRITE(TIS_REG_ACCESS, data);
Julius Werner2f37bd62015-02-19 14:51:15 -0800187 write8(TIS_REG(locality, TIS_REG_ACCESS), data);
Aaron Durbinc4220022013-07-24 16:02:14 -0500188}
189
Sergii Dmytruk76086992022-10-30 17:19:46 +0200190static inline u32 tpm_read_intf_cap(int locality)
191{
192 u32 value = read32(TIS_REG(locality, TIS_REG_INTF_CAPABILITY));
193 TPM_DEBUG_IO_READ(TIS_REG_INTF_CAPABILITY, value);
194 return value;
195}
196
197static inline u32 tpm_read_intf_id(int locality)
198{
199 u32 value = read32(TIS_REG(locality, TIS_REG_INTF_ID));
200 TPM_DEBUG_IO_READ(TIS_REG_INTF_ID, value);
201 return value;
202}
203
Aaron Durbinc4220022013-07-24 16:02:14 -0500204static inline u32 tpm_read_did_vid(int locality)
205{
Julius Werner2f37bd62015-02-19 14:51:15 -0800206 u32 value = read32(TIS_REG(locality, TIS_REG_DID_VID));
Aaron Durbinc4220022013-07-24 16:02:14 -0500207 TPM_DEBUG_IO_READ(TIS_REG_DID_VID, value);
208 return value;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700209}
210
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700211static inline void tpm_write_int_vector(int vector, int locality)
212{
213 TPM_DEBUG_IO_WRITE(TIS_REG_INT_VECTOR, vector);
Julius Werner2f37bd62015-02-19 14:51:15 -0800214 write8(TIS_REG(locality, TIS_REG_INT_VECTOR), vector & 0xf);
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700215}
216
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530217static inline u8 tpm_read_int_vector(int locality)
218{
219 u8 value = read8(TIS_REG(locality, TIS_REG_INT_VECTOR));
220 TPM_DEBUG_IO_READ(TIS_REG_INT_VECTOR, value);
221 return value;
222}
223
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700224static inline void tpm_write_int_polarity(int polarity, int locality)
225{
226 /* Set polarity and leave all other bits at 0 */
227 u32 value = (polarity & 0x3) << 3;
228 TPM_DEBUG_IO_WRITE(TIS_REG_INT_ENABLE, value);
Julius Werner2f37bd62015-02-19 14:51:15 -0800229 write32(TIS_REG(locality, TIS_REG_INT_ENABLE), value);
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700230}
231
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530232static inline u32 tpm_read_int_polarity(int locality)
233{
234 /* Get polarity and leave all other bits */
235 u32 value = read8(TIS_REG(locality, TIS_REG_INT_ENABLE));
236 value = (value >> 3) & 0x3;
237 TPM_DEBUG_IO_READ(TIS_REG_INT_ENABLE, value);
238 return value;
239}
240
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700241/*
Aaron Durbinc4220022013-07-24 16:02:14 -0500242 * tis_wait_sts()
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700243 *
Werner Zeh92ab6112021-10-11 15:27:12 +0200244 * Wait for at most a second for a status to change its state to match the
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700245 * expected state. Normally the transition happens within microseconds.
246 *
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700247 * @locality - locality
248 * @mask - bitmask for the bitfield(s) to watch
249 * @expected - value the field(s) are supposed to be set to
250 *
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600251 * Returns TPM_SUCCESS on success or TPM_CB_TIMEOUT on timeout.
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700252 */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600253static tpm_result_t tis_wait_sts(int locality, u8 mask, u8 expected)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700254{
Werner Zeh92ab6112021-10-11 15:27:12 +0200255 struct stopwatch sw;
256
257 stopwatch_init_usecs_expire(&sw, MAX_DELAY_US);
258 do {
Aaron Durbinc4220022013-07-24 16:02:14 -0500259 u8 value = tpm_read_status(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700260 if ((value & mask) == expected)
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600261 return TPM_SUCCESS;
Werner Zeh92ab6112021-10-11 15:27:12 +0200262 udelay(1);
263 } while (!stopwatch_expired(&sw));
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600264 return TPM_CB_TIMEOUT;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700265}
266
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600267static inline tpm_result_t tis_wait_ready(int locality)
Aaron Durbinc4220022013-07-24 16:02:14 -0500268{
269 return tis_wait_sts(locality, TIS_STS_COMMAND_READY,
270 TIS_STS_COMMAND_READY);
271}
272
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600273static inline tpm_result_t tis_wait_valid(int locality)
Aaron Durbinc4220022013-07-24 16:02:14 -0500274{
275 return tis_wait_sts(locality, TIS_STS_VALID, TIS_STS_VALID);
276}
277
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600278static inline tpm_result_t tis_wait_valid_data(int locality)
Aaron Durbinc4220022013-07-24 16:02:14 -0500279{
280 const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
281 return tis_wait_sts(locality, has_data, has_data);
282}
283
284static inline int tis_has_valid_data(int locality)
285{
286 const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
287 return (tpm_read_status(locality) & has_data) == has_data;
288}
289
290static inline int tis_expect_data(int locality)
291{
292 return !!(tpm_read_status(locality) & TIS_STS_EXPECT);
293}
294
295/*
296 * tis_wait_access()
297 *
Werner Zeh92ab6112021-10-11 15:27:12 +0200298 * Wait for at most a second for a access to change its state to match the
Aaron Durbinc4220022013-07-24 16:02:14 -0500299 * expected state. Normally the transition happens within microseconds.
300 *
301 * @locality - locality
302 * @mask - bitmask for the bitfield(s) to watch
303 * @expected - value the field(s) are supposed to be set to
304 *
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600305 * Returns TPM_SUCCESS on success or TPM_CB_TIMEOUT on timeout.
Aaron Durbinc4220022013-07-24 16:02:14 -0500306 */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600307static tpm_result_t tis_wait_access(int locality, u8 mask, u8 expected)
Aaron Durbinc4220022013-07-24 16:02:14 -0500308{
Werner Zeh92ab6112021-10-11 15:27:12 +0200309 struct stopwatch sw;
310
311 stopwatch_init_usecs_expire(&sw, MAX_DELAY_US);
312 do {
Aaron Durbinc4220022013-07-24 16:02:14 -0500313 u8 value = tpm_read_access(locality);
314 if ((value & mask) == expected)
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600315 return TPM_SUCCESS;
Werner Zeh92ab6112021-10-11 15:27:12 +0200316 udelay(1);
317 } while (!stopwatch_expired(&sw));
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600318 return TPM_CB_TIMEOUT;
Aaron Durbinc4220022013-07-24 16:02:14 -0500319}
320
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600321static inline tpm_result_t tis_wait_received_access(int locality)
Aaron Durbinc4220022013-07-24 16:02:14 -0500322{
323 return tis_wait_access(locality, TIS_ACCESS_ACTIVE_LOCALITY,
324 TIS_ACCESS_ACTIVE_LOCALITY);
325}
326
327static inline int tis_has_access(int locality)
328{
329 return !!(tpm_read_access(locality) & TIS_ACCESS_ACTIVE_LOCALITY);
330}
331
332static inline void tis_request_access(int locality)
333{
334 tpm_write_access(TIS_ACCESS_REQUEST_USE, locality);
335}
336
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700337/*
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700338 * PC Client Specific TPM Interface Specification section 11.2.12:
339 *
340 * Software must be prepared to send two writes of a "1" to command ready
341 * field: the first to indicate successful read of all the data, thus
342 * clearing the data from the ReadFIFO and freeing the TPM's resources,
343 * and the second to indicate to the TPM it is about to send a new command.
344 *
345 * In practice not all TPMs behave the same so it is necessary to be
346 * flexible when trying to set command ready.
347 *
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700348 */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600349static tpm_result_t tis_command_ready(u8 locality)
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700350{
351 u32 status;
352
353 /* 1st attempt to set command ready */
Aaron Durbinc4220022013-07-24 16:02:14 -0500354 tpm_write_status(TIS_STS_COMMAND_READY, locality);
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700355
356 /* Wait for response */
Aaron Durbinc4220022013-07-24 16:02:14 -0500357 status = tpm_read_status(locality);
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700358
359 /* Check if command ready is set yet */
360 if (status & TIS_STS_COMMAND_READY)
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600361 return TPM_SUCCESS;
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700362
363 /* 2nd attempt to set command ready */
Aaron Durbinc4220022013-07-24 16:02:14 -0500364 tpm_write_status(TIS_STS_COMMAND_READY, locality);
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700365
Aaron Durbinc4220022013-07-24 16:02:14 -0500366 return tis_wait_ready(locality);
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700367}
368
369/*
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300370 * pc80_tis_probe()
Jon Murphya7c64d72023-09-15 07:58:08 -0600371 *
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700372 * Probe the TPM device and try determining its manufacturer/device name.
373 *
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600374 * Returns TPM_SUCCESS on success (the device is found or was found during
375 * an earlier invocation) or TPM_CB_FAIL if the device is not found.
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700376 */
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200377static tpm_result_t pc80_tis_probe(enum tpm_family *family)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700378{
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200379 static enum tpm_family tpm_family;
380
Sergii Dmytruk76086992022-10-30 17:19:46 +0200381 const char *device_name = NULL;
382 const char *vendor_name = NULL;
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700383 const struct device_name *dev;
Sergii Dmytruk76086992022-10-30 17:19:46 +0200384 u32 didvid, intf_id;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700385 u16 vid, did;
Sergii Dmytruk76086992022-10-30 17:19:46 +0200386 u8 locality = 0, intf_type;
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700387 int i;
Sergii Dmytruk76086992022-10-30 17:19:46 +0200388 const char *family_str;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700389
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200390 if (vendor_dev_id) {
391 if (family != NULL)
392 *family = tpm_family;
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600393 return TPM_SUCCESS; /* Already probed. */
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200394 }
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700395
Aaron Durbinc4220022013-07-24 16:02:14 -0500396 didvid = tpm_read_did_vid(0);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700397 if (!didvid || (didvid == 0xffffffff)) {
Angel Pons08e8cab2020-06-18 15:20:37 +0200398 printf("%s: No TPM device found\n", __func__);
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600399 return TPM_CB_FAIL;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700400 }
401
Sergii Dmytruk76086992022-10-30 17:19:46 +0200402 intf_id = tpm_read_intf_id(locality);
403 intf_type = (intf_id & 0xf);
404 if (intf_type == 0xf) {
405 u32 intf_cap = tpm_read_intf_cap(locality);
406 u8 intf_version = (intf_cap >> 28) & 0x7;
407 switch (intf_version) {
408 case 0:
409 case 2:
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200410 tpm_family = TPM_1;
Sergii Dmytruk76086992022-10-30 17:19:46 +0200411 break;
412 case 3:
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200413 tpm_family = TPM_2;
Sergii Dmytruk76086992022-10-30 17:19:46 +0200414 break;
415 default:
416 printf("%s: Unexpected TPM interface version: %d\n", __func__,
417 intf_version);
418 return TPM_CB_PROBE_FAILURE;
419 }
420 } else if (intf_type == 0) {
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200421 tpm_family = TPM_2;
Sergii Dmytruk76086992022-10-30 17:19:46 +0200422 } else {
423 printf("%s: Unexpected TPM interface type: %d\n", __func__, intf_type);
424 return TPM_CB_PROBE_FAILURE;
425 }
426
Arthur Heymans0ca944b2019-11-20 19:51:06 +0100427 vendor_dev_id = didvid;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700428
429 vid = didvid & 0xffff;
430 did = (didvid >> 16) & 0xffff;
431 for (i = 0; i < ARRAY_SIZE(vendor_names); i++) {
432 int j = 0;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700433 if (vid == vendor_names[i].vendor_id) {
434 vendor_name = vendor_names[i].vendor_name;
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700435 } else {
436 continue;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700437 }
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700438 dev = &vendor_names[i].dev_names[j];
Sergii Dmytruk76086992022-10-30 17:19:46 +0200439 while (dev->dev_id != 0xffff) {
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200440 if (dev->dev_id == did && dev->family == tpm_family) {
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700441 device_name = dev->dev_name;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700442 break;
443 }
444 j++;
Subrata41b08d92015-05-14 14:38:07 +0530445 dev = &vendor_names[i].dev_names[j];
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700446 }
447 break;
448 }
Sergii Dmytruk76086992022-10-30 17:19:46 +0200449
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200450 family_str = (tpm_family == TPM_1 ? "TPM 1.2" : "TPM 2.0");
Sergii Dmytruk76086992022-10-30 17:19:46 +0200451 if (vendor_name == NULL) {
452 printk(BIOS_INFO, "Found %s 0x%04x by 0x%04x\n", family_str, did, vid);
453 } else if (device_name == NULL) {
454 printk(BIOS_INFO, "Found %s 0x%04x by %s (0x%04x)\n", family_str, did,
455 vendor_name, vid);
456 } else {
457 printk(BIOS_INFO, "Found %s %s (0x%04x) by %s (0x%04x)\n", family_str,
458 device_name, did, vendor_name, vid);
459 }
460
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200461 if (family != NULL)
462 *family = tpm_family;
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600463 return TPM_SUCCESS;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700464}
465
466/*
467 * tis_senddata()
468 *
469 * send the passed in data to the TPM device.
470 *
471 * @data - address of the data to send, byte by byte
472 * @len - length of the data to send
473 *
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600474 * Returns TPM_SUCCESS on success, TPM_CB_FAIL on error (in case the device does
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700475 * not accept the entire command).
476 */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600477static tpm_result_t tis_senddata(const u8 *const data, u32 len)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700478{
479 u32 offset = 0;
480 u16 burst = 0;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700481 u8 locality = 0;
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600482 tpm_result_t rc = TPM_SUCCESS;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700483
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600484 rc = tis_wait_ready(locality);
485 if (rc) {
486 printf("%s:%d - failed to get 'command_ready' status with error %#x\n",
487 __FILE__, __LINE__, rc);
488 return rc;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700489 }
Aaron Durbinc4220022013-07-24 16:02:14 -0500490 burst = tpm_read_burst_count(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700491
492 while (1) {
Martin Roth38ddbfb2019-10-23 21:41:00 -0600493 unsigned int count;
Werner Zeh92ab6112021-10-11 15:27:12 +0200494 struct stopwatch sw;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700495
496 /* Wait till the device is ready to accept more data. */
Werner Zeh92ab6112021-10-11 15:27:12 +0200497 stopwatch_init_usecs_expire(&sw, MAX_DELAY_US);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700498 while (!burst) {
Werner Zeh92ab6112021-10-11 15:27:12 +0200499 if (stopwatch_expired(&sw)) {
Werner Zeh66b2f202021-10-12 15:14:22 +0200500 printf("%s:%d failed to feed %u bytes of %u\n",
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700501 __FILE__, __LINE__, len - offset, len);
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600502 return TPM_CB_TIMEOUT;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700503 }
504 udelay(1);
Aaron Durbinc4220022013-07-24 16:02:14 -0500505 burst = tpm_read_burst_count(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700506 }
507
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700508 /*
509 * Calculate number of bytes the TPM is ready to accept in one
510 * shot.
511 *
512 * We want to send the last byte outside of the loop (hence
513 * the -1 below) to make sure that the 'expected' status bit
514 * changes to zero exactly after the last byte is fed into the
515 * FIFO.
516 */
Elyes HAOUAS361a9352019-12-18 21:26:33 +0100517 count = MIN(burst, len - offset - 1);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700518 while (count--)
Aaron Durbinc4220022013-07-24 16:02:14 -0500519 tpm_write_data(data[offset++], locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700520
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600521 rc = tis_wait_valid(locality);
522 if (rc || !tis_expect_data(locality)) {
523 printf("%s:%d TPM command feed overflow with error %#x\n",
524 __FILE__, __LINE__, rc);
525 return rc ? rc : TPM_CB_FAIL;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700526 }
527
Aaron Durbinc4220022013-07-24 16:02:14 -0500528 burst = tpm_read_burst_count(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700529 if ((offset == (len - 1)) && burst)
530 /*
531 * We need to be able to send the last byte to the
532 * device, so burst size must be nonzero before we
533 * break out.
534 */
535 break;
536 }
537
538 /* Send the last byte. */
Aaron Durbinc4220022013-07-24 16:02:14 -0500539 tpm_write_data(data[offset++], locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700540
541 /*
542 * Verify that TPM does not expect any more data as part of this
543 * command.
544 */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600545 rc = tis_wait_valid(locality);
546 if (rc || tis_expect_data(locality)) {
547 printf("%s:%d unexpected TPM error %#x with status %#x\n",
548 __FILE__, __LINE__, rc, tpm_read_status(locality));
549 return rc ? rc : TPM_CB_FAIL;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700550 }
551
552 /* OK, sitting pretty, let's start the command execution. */
Aaron Durbinc4220022013-07-24 16:02:14 -0500553 tpm_write_status(TIS_STS_TPM_GO, locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700554
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600555 return TPM_SUCCESS;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700556}
557
558/*
559 * tis_readresponse()
560 *
561 * read the TPM device response after a command was issued.
562 *
563 * @buffer - address where to read the response, byte by byte.
564 * @len - pointer to the size of buffer
565 *
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600566 * On success stores the number of received bytes to len and returns
567 * TPM_SUCCESS. On errors (misformatted TPM data or synchronization
568 * problems) returns TPM_CB_FAIL.
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700569 */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600570static tpm_result_t tis_readresponse(u8 *buffer, size_t *len)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700571{
572 u16 burst_count;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700573 u32 offset = 0;
574 u8 locality = 0;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700575 u32 expected_count = *len;
576 int max_cycles = 0;
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600577 tpm_result_t rc = TPM_SUCCESS;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700578
579 /* Wait for the TPM to process the command */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600580 rc = tis_wait_valid_data(locality);
581 if (rc) {
582 printf("%s:%d failed processing command with error %#x\n",
583 __FILE__, __LINE__, rc);
584 return rc;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700585 }
586
587 do {
Aaron Durbinc4220022013-07-24 16:02:14 -0500588 while ((burst_count = tpm_read_burst_count(locality)) == 0) {
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700589 if (max_cycles++ == MAX_DELAY_US) {
590 printf("%s:%d TPM stuck on read\n",
591 __FILE__, __LINE__);
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600592 return TPM_CB_FAIL;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700593 }
594 udelay(1);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700595 }
596
597 max_cycles = 0;
598
599 while (burst_count-- && (offset < expected_count)) {
Aaron Durbinc4220022013-07-24 16:02:14 -0500600 buffer[offset++] = tpm_read_data(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700601 if (offset == 6) {
602 /*
603 * We got the first six bytes of the reply,
604 * let's figure out how many bytes to expect
605 * total - it is stored as a 4 byte number in
606 * network order, starting with offset 2 into
607 * the body of the reply.
608 */
609 u32 real_length;
610 memcpy(&real_length,
611 buffer + 2,
612 sizeof(real_length));
613 expected_count = be32_to_cpu(real_length);
614
615 if ((expected_count < offset) ||
616 (expected_count > *len)) {
Werner Zeh66b2f202021-10-12 15:14:22 +0200617 printf("%s:%d bad response size %u\n",
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700618 __FILE__, __LINE__,
619 expected_count);
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600620 return TPM_CB_FAIL;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700621 }
622 }
623 }
624
625 /* Wait for the next portion */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600626 rc = tis_wait_valid(locality);
627 if (rc) {
628 printf("%s:%d failed to read response with error %#x\n",
629 __FILE__, __LINE__, rc);
630 return rc;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700631 }
632
633 if (offset == expected_count)
634 break; /* We got all we need */
635
Bill XIEa4bf0b72018-03-22 17:07:43 +0800636 /*
637 * Certain TPMs seem to need some delay between tis_wait_valid()
638 * and tis_has_valid_data(), or some race-condition-related
639 * issue will occur.
640 */
Julius Wernercd49cce2019-03-05 16:53:33 -0800641 if (CONFIG(TPM_RDRESP_NEED_DELAY))
Bill XIEa4bf0b72018-03-22 17:07:43 +0800642 udelay(10);
643
Aaron Durbinc4220022013-07-24 16:02:14 -0500644 } while (tis_has_valid_data(locality));
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700645
Aaron Durbinc4220022013-07-24 16:02:14 -0500646 /* * Make sure we indeed read all there was. */
647 if (tis_has_valid_data(locality)) {
Jon Murphy53fc6672023-09-26 21:05:37 -0600648 printf("%s:%d wrong receive status: %#x %u bytes left\n",
Aaron Durbinc4220022013-07-24 16:02:14 -0500649 __FILE__, __LINE__, tpm_read_status(locality),
650 tpm_read_burst_count(locality));
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600651 return TPM_CB_FAIL;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700652 }
653
654 /* Tell the TPM that we are done. */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600655 rc = tis_command_ready(locality);
656 if (rc)
657 return rc;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700658
659 *len = offset;
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600660 return TPM_SUCCESS;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700661}
662
663/*
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300664 * pc80_tis_open()
Sergii Dmytruk4ee03172022-12-22 19:35:25 +0200665 *
Sergii Dmytruk0a89d522022-10-29 16:57:07 +0300666 * Requests access to locality 0 for the caller.
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700667 *
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600668 * Returns TPM_SUCCESS on success, TSS Error on failure.
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700669 */
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300670static tpm_result_t pc80_tis_open(void)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700671{
672 u8 locality = 0; /* we use locality zero for everything */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600673 tpm_result_t rc = TPM_SUCCESS;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700674
Sergii Dmytruk0a89d522022-10-29 16:57:07 +0300675 if (!tis_has_access(locality)) {
676 /* request access to locality */
677 tis_request_access(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700678
Sergii Dmytruk0a89d522022-10-29 16:57:07 +0300679 /* did we get a lock? */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600680 rc = tis_wait_received_access(locality);
681 if (rc) {
682 printf("%s:%d - failed to lock locality %u with error %#x\n",
683 __FILE__, __LINE__, locality, rc);
684 return rc;
Sergii Dmytruk0a89d522022-10-29 16:57:07 +0300685 }
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700686
Sergii Dmytruk0a89d522022-10-29 16:57:07 +0300687 /* Certain TPMs seem to need some delay here or they hang... */
688 udelay(10);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700689 }
690
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600691 return tis_command_ready(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700692}
693
694/*
Sergii Dmytruk4ee03172022-12-22 19:35:25 +0200695 * tis_sendrecv()
696 *
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700697 * Send the requested data to the TPM and then try to get its response
698 *
699 * @sendbuf - buffer of the data to send
700 * @send_size size of the data to send
701 * @recvbuf - memory to save the response to
702 * @recv_len - pointer to the size of the response buffer
703 *
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600704 * Returns TPM_SUCCESS on success (and places the number of response bytes
705 * at recv_len) or TPM_CB_FAIL on failure.
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700706 */
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300707static tpm_result_t pc80_tpm_sendrecv(const uint8_t *sendbuf, size_t send_size,
708 uint8_t *recvbuf, size_t *recv_len)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700709{
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600710 tpm_result_t rc = tis_senddata(sendbuf, send_size);
711 if (rc) {
712 printf("%s:%d failed sending data to TPM with error %#x\n",
713 __FILE__, __LINE__, rc);
714 return rc;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700715 }
716
717 return tis_readresponse(recvbuf, recv_len);
718}
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700719
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700720/*
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300721 * tis_probe()
722 *
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200723 * Probe for the TPM device and set it up for use within locality 0.
724 *
725 * @tpm_family - pointer to tpm_family which is set to TPM family of the device.
726 *
727 * Returns pointer to send-receive function on success or NULL on failure.
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300728 */
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200729tis_sendrecv_fn tis_probe(enum tpm_family *family)
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300730{
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200731 if (pc80_tis_probe(family))
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300732 return NULL;
733
734 if (pc80_tis_open())
735 return NULL;
736
737 return &pc80_tpm_sendrecv;
738}
739
740/*
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700741 * tis_setup_interrupt()
742 *
743 * Set up the interrupt vector and polarity for locality 0 and
744 * disable all interrupts so they are unused in firmware but can
745 * be enabled by the OS.
746 *
747 * The values used here must match what is passed in the TPM ACPI
748 * device if ACPI is used on the platform.
749 *
750 * @vector - TPM interrupt vector
751 * @polarity - TPM interrupt polarity
752 *
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600753 * Returns TPM_SUCCESS on success, TPM_CB_FAIL on failure.
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700754 */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600755static tpm_result_t tis_setup_interrupt(int vector, int polarity)
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700756{
757 u8 locality = 0;
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600758 tpm_result_t rc = tlcl_lib_init();
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700759
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600760 if (rc)
761 return rc;
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700762
763 /* Set TPM interrupt vector */
764 tpm_write_int_vector(vector, locality);
765
Elyes HAOUAS18958382018-08-07 12:23:16 +0200766 /* Set TPM interrupt polarity and disable interrupts */
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700767 tpm_write_int_polarity(polarity, locality);
768
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600769 return TPM_SUCCESS;
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700770}
771
772static void lpc_tpm_read_resources(struct device *dev)
773{
774 /* Static 5K memory region specified in Kconfig */
Arthur Heymanse05693e2023-07-05 11:43:09 +0200775 mmio_range(dev, 0, CONFIG_TPM_TIS_BASE_ADDRESS, 0x5000);
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700776}
777
778static void lpc_tpm_set_resources(struct device *dev)
779{
780 tpm_config_t *config = (tpm_config_t *)dev->chip_info;
Kyösti Mälkkibaa16e92019-11-05 18:38:00 +0200781 DEVTREE_CONST struct resource *res;
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700782
783 for (res = dev->resource_list; res; res = res->next) {
784 if (!(res->flags & IORESOURCE_ASSIGNED))
785 continue;
786
787 if (res->flags & IORESOURCE_IRQ) {
788 /* Set interrupt vector */
789 tis_setup_interrupt((int)res->base,
790 config->irq_polarity);
791 } else {
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700792 continue;
793 }
794
Kyösti Mälkkibaa16e92019-11-05 18:38:00 +0200795#if !DEVTREE_EARLY
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700796 res->flags |= IORESOURCE_STORED;
797 report_resource_stored(dev, res, " <tpm>");
Kyösti Mälkkibaa16e92019-11-05 18:38:00 +0200798#endif
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700799 }
800}
801
Julius Wernercd49cce2019-03-05 16:53:33 -0800802#if CONFIG(HAVE_ACPI_TABLES)
Furquan Shaikh7536a392020-04-24 21:59:21 -0700803static void lpc_tpm_fill_ssdt(const struct device *dev)
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530804{
Michał Kopeća691cbd2022-02-22 12:30:22 +0100805 /* Windows 11 requires the following path for TPM to be detected */
806 const char *path = "\\_SB_.PCI0";
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530807
808 /* Device */
809 acpigen_write_scope(path);
810 acpigen_write_device(acpi_device_name(dev));
811
Michał Żygowski7b288012020-03-20 15:41:44 +0100812 if (CONFIG(TPM2)) {
813 acpigen_write_name_string("_HID", "MSFT0101");
814 acpigen_write_name_string("_CID", "MSFT0101");
815 } else {
816 acpigen_write_name("_HID");
817 acpigen_emit_eisaid("PNP0C31");
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530818
Michał Żygowski7b288012020-03-20 15:41:44 +0100819 acpigen_write_name("_CID");
820 acpigen_emit_eisaid("PNP0C31");
821 }
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530822
Patrick Rudolphc83bab62019-12-13 12:16:06 +0100823 acpi_device_write_uid(dev);
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530824
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530825 u32 did_vid = tpm_read_did_vid(0);
826 if (did_vid > 0 && did_vid < 0xffffffff)
827 acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON);
828 else
829 acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_OFF);
830
Kevin Cody-Littlec97b5af2018-05-09 14:14:59 -0400831 u16 port = dev->path.pnp.port;
832
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530833 /* Resources */
834 acpigen_write_name("_CRS");
835 acpigen_write_resourcetemplate_header();
836 acpigen_write_mem32fixed(1, CONFIG_TPM_TIS_BASE_ADDRESS, 0x5000);
Frans Hendriks6cc937e2018-10-29 14:30:58 +0100837 if (port)
838 acpigen_write_io16(port, port, 1, 2, 1);
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530839
840 if (CONFIG_TPM_PIRQ) {
841 /*
842 * PIRQ: Update interrupt vector with configured PIRQ
843 * Active-Low Level-Triggered Shared
844 */
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800845 struct acpi_irq tpm_irq_a = ACPI_IRQ_LEVEL_LOW(CONFIG_TPM_PIRQ);
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530846 acpi_device_write_interrupt(&tpm_irq_a);
847 } else if (tpm_read_int_vector(0) > 0) {
848 u8 int_vec = tpm_read_int_vector(0);
849 u8 int_pol = tpm_read_int_polarity(0);
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800850 struct acpi_irq tpm_irq = ACPI_IRQ_LEVEL_LOW(int_vec);
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530851
852 if (int_pol & 1)
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800853 tpm_irq.polarity = ACPI_IRQ_ACTIVE_LOW;
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530854 else
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800855 tpm_irq.polarity = ACPI_IRQ_ACTIVE_HIGH;
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530856
857 if (int_pol & 2)
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800858 tpm_irq.mode = ACPI_IRQ_EDGE_TRIGGERED;
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530859 else
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800860 tpm_irq.mode = ACPI_IRQ_LEVEL_TRIGGERED;
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530861
862 acpi_device_write_interrupt(&tpm_irq);
863 }
864
Patrick Rudolphd8d8be12020-09-21 09:48:53 +0200865
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530866 acpigen_write_resourcetemplate_footer();
867
Patrick Rudolphd8d8be12020-09-21 09:48:53 +0200868 if (!CONFIG(CHROMEOS))
869 tpm_ppi_acpi_fill_ssdt(dev);
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530870
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530871 acpigen_pop_len(); /* Device */
872 acpigen_pop_len(); /* Scope */
873
Kyösti Mälkkibaa16e92019-11-05 18:38:00 +0200874#if !DEVTREE_EARLY
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530875 printk(BIOS_INFO, "%s.%s: %s %s\n", path, acpi_device_name(dev),
876 dev->chip_ops->name, dev_path(dev));
Kyösti Mälkkibaa16e92019-11-05 18:38:00 +0200877#endif
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530878}
879
Aaron Durbinaa090cb2017-09-13 16:01:52 -0600880static const char *lpc_tpm_acpi_name(const struct device *dev)
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530881{
882 return "TPM";
883}
884#endif
885
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700886static struct device_operations lpc_tpm_ops = {
Elyes HAOUAS2aa3b162018-11-27 17:02:10 +0100887 .read_resources = lpc_tpm_read_resources,
888 .set_resources = lpc_tpm_set_resources,
Julius Wernercd49cce2019-03-05 16:53:33 -0800889#if CONFIG(HAVE_ACPI_TABLES)
Nico Huber68680dd2020-03-31 17:34:52 +0200890 .acpi_name = lpc_tpm_acpi_name,
891 .acpi_fill_ssdt = lpc_tpm_fill_ssdt,
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530892#endif
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700893};
894
Arthur Heymans04c49a52023-08-22 13:16:40 +0200895static struct device_operations noop_tpm_ops = {
896 .read_resources = noop_read_resources,
897 .set_resources = noop_set_resources,
898};
899
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700900static struct pnp_info pnp_dev_info[] = {
901 { .flags = PNP_IRQ0 }
902};
903
904static void enable_dev(struct device *dev)
905{
Kyösti Mälkkid2b2a182021-04-29 15:33:07 +0300906 if (CONFIG(TPM))
Kyösti Mälkki9cc64932020-05-29 19:42:07 +0300907 pnp_enable_devices(dev, &lpc_tpm_ops,
908 ARRAY_SIZE(pnp_dev_info), pnp_dev_info);
Arthur Heymans04c49a52023-08-22 13:16:40 +0200909 else
910 pnp_enable_devices(dev, &noop_tpm_ops, ARRAY_SIZE(pnp_dev_info), pnp_dev_info);
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700911}
912
913struct chip_operations drivers_pc80_tpm_ops = {
Nicholas Sudsgaardbfb11be2024-01-30 09:53:46 +0900914 .name = "LPC TPM",
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700915 .enable_dev = enable_dev
916};