blob: f9227143819a99807b44e0fe097eb8c9abdf3e40 [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>
Sergii Dmytruk3e5cefc2022-11-01 00:48:43 +020027
Duncan Lauriedd281ed2014-10-30 15:20:19 -070028#include "chip.h"
Sergii Dmytruk3e5cefc2022-11-01 00:48:43 +020029#include "tpm.h"
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070030
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070031#define PREFIX "lpc_tpm: "
Patrick Rudolphd8d8be12020-09-21 09:48:53 +020032
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070033/* coreboot wrapper for TPM driver (start) */
34#define TPM_DEBUG(fmt, args...) \
Julius Wernercd49cce2019-03-05 16:53:33 -080035 if (CONFIG(DEBUG_TPM)) { \
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070036 printk(BIOS_DEBUG, PREFIX); \
Elyes HAOUASa342f392018-10-17 10:56:26 +020037 printk(BIOS_DEBUG, fmt, ##args); \
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070038 }
Aaron Durbinc4220022013-07-24 16:02:14 -050039#define TPM_DEBUG_IO_READ(reg_, val_) \
Jon Murphy53fc6672023-09-26 21:05:37 -060040 TPM_DEBUG("Read reg %#x returns %#x\n", (reg_), (val_))
Aaron Durbinc4220022013-07-24 16:02:14 -050041#define TPM_DEBUG_IO_WRITE(reg_, val_) \
Jon Murphy53fc6672023-09-26 21:05:37 -060042 TPM_DEBUG("Write reg %#x with %#x\n", (reg_), (val_))
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070043#define printf(x...) printk(BIOS_ERR, x)
44
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070045/* coreboot wrapper for TPM driver (end) */
46
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070047/* the macro accepts the locality value, but only locality 0 is operational */
48#define TIS_REG(LOCALITY, REG) \
Patrick Rudolph56fdafb2020-07-01 20:07:08 +020049 (void *)(uintptr_t)(CONFIG_TPM_TIS_BASE_ADDRESS + (LOCALITY << 12) + REG)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070050
51/* hardware registers' offsets */
52#define TIS_REG_ACCESS 0x0
53#define TIS_REG_INT_ENABLE 0x8
54#define TIS_REG_INT_VECTOR 0xc
55#define TIS_REG_INT_STATUS 0x10
56#define TIS_REG_INTF_CAPABILITY 0x14
57#define TIS_REG_STS 0x18
Aaron Durbinc4220022013-07-24 16:02:14 -050058#define TIS_REG_BURST_COUNT 0x19
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070059#define TIS_REG_DATA_FIFO 0x24
Sergii Dmytruk76086992022-10-30 17:19:46 +020060#define TIS_REG_INTF_ID 0x30
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070061#define TIS_REG_DID_VID 0xf00
62#define TIS_REG_RID 0xf04
63
64/* Some registers' bit field definitions */
65#define TIS_STS_VALID (1 << 7) /* 0x80 */
66#define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */
67#define TIS_STS_TPM_GO (1 << 5) /* 0x20 */
68#define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */
69#define TIS_STS_EXPECT (1 << 3) /* 0x08 */
70#define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */
71
72#define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */
73#define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */
74#define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */
75#define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */
76#define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */
77#define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */
78#define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */
79
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070080 /* 1 second is plenty for anything TPM does.*/
Werner Zeh92ab6112021-10-11 15:27:12 +020081#define MAX_DELAY_US USECS_PER_SEC
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070082
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070083/*
84 * Structures defined below allow creating descriptions of TPM vendor/device
Sergii Dmytruk76086992022-10-30 17:19:46 +020085 * ID information for run time discovery.
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070086 */
87struct device_name {
88 u16 dev_id;
Sergii Dmytruk76086992022-10-30 17:19:46 +020089 enum tpm_family family;
Elyes HAOUASb0b0c8c2018-07-08 12:33:47 +020090 const char *const dev_name;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070091};
92
93struct vendor_name {
94 u16 vendor_id;
Elyes HAOUASb0b0c8c2018-07-08 12:33:47 +020095 const char *vendor_name;
96 const struct device_name *dev_names;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070097};
98
Stefan Reinauerc908fc72012-04-30 16:33:44 -070099static const struct device_name atmel_devices[] = {
Sergii Dmytruk76086992022-10-30 17:19:46 +0200100 {0x3204, TPM_1, "AT97SC3204"},
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700101 {0xffff}
102};
103
Stefan Reinauerc668af72011-10-27 21:28:25 +0000104static const struct device_name infineon_devices[] = {
Sergii Dmytruk76086992022-10-30 17:19:46 +0200105 {0x000b, TPM_1, "SLB9635 TT 1.2"},
106 {0x001a, TPM_1, "SLB9660 TT 1.2"},
107 {0x001b, TPM_1, "SLB9670 TT 1.2"},
108 {0x001a, TPM_2, "SLB9665 TT 2.0"},
109 {0x001b, TPM_2, "SLB9670 TT 2.0"},
110 {0x001d, TPM_2, "SLB9672 TT 2.0"},
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700111 {0xffff}
112};
113
114static const struct device_name nuvoton_devices[] = {
Sergii Dmytruk76086992022-10-30 17:19:46 +0200115 {0x00fe, TPM_1, "NPCT420AA V2"},
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700116 {0xffff}
117};
118
119static const struct device_name stmicro_devices[] = {
Sergii Dmytruk76086992022-10-30 17:19:46 +0200120 {0x0000, TPM_1, "ST33ZP24" },
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700121 {0xffff}
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700122};
123
Tsung Ho Wu804a0432019-06-07 15:03:49 -0700124static const struct device_name swtpm_devices[] = {
Sergii Dmytruk76086992022-10-30 17:19:46 +0200125 {0x0001, TPM_1, "SwTPM 1.2" },
126 {0x0001, TPM_2, "SwTPM 2.0" },
Tsung Ho Wu804a0432019-06-07 15:03:49 -0700127 {0xffff}
128};
129
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700130static const struct vendor_name vendor_names[] = {
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700131 {0x1114, "Atmel", atmel_devices},
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700132 {0x15d1, "Infineon", infineon_devices},
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700133 {0x1050, "Nuvoton", nuvoton_devices},
Tsung Ho Wu804a0432019-06-07 15:03:49 -0700134 {0x1014, "TPM Emulator", swtpm_devices},
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700135 {0x104a, "ST Microelectronics", stmicro_devices},
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700136};
137
138/*
139 * Cached vendor/device ID pair to indicate that the device has been already
140 * discovered
141 */
Arthur Heymans0ca944b2019-11-20 19:51:06 +0100142static u32 vendor_dev_id;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700143
Aaron Durbinc4220022013-07-24 16:02:14 -0500144static inline u8 tpm_read_status(int locality)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700145{
Julius Werner2f37bd62015-02-19 14:51:15 -0800146 u8 value = read8(TIS_REG(locality, TIS_REG_STS));
Aaron Durbinc4220022013-07-24 16:02:14 -0500147 TPM_DEBUG_IO_READ(TIS_REG_STS, value);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700148 return value;
149}
150
Aaron Durbinc4220022013-07-24 16:02:14 -0500151static inline void tpm_write_status(u8 sts, int locality)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700152{
Aaron Durbinc4220022013-07-24 16:02:14 -0500153 TPM_DEBUG_IO_WRITE(TIS_REG_STS, sts);
Julius Werner2f37bd62015-02-19 14:51:15 -0800154 write8(TIS_REG(locality, TIS_REG_STS), sts);
Aaron Durbinc4220022013-07-24 16:02:14 -0500155}
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700156
Aaron Durbinc4220022013-07-24 16:02:14 -0500157static inline u8 tpm_read_data(int locality)
158{
Julius Werner2f37bd62015-02-19 14:51:15 -0800159 u8 value = read8(TIS_REG(locality, TIS_REG_DATA_FIFO));
Aaron Durbinc4220022013-07-24 16:02:14 -0500160 TPM_DEBUG_IO_READ(TIS_REG_DATA_FIFO, value);
161 return value;
162}
163
164static inline void tpm_write_data(u8 data, int locality)
165{
Werner Zeha31d6cd2021-10-07 07:15:38 +0200166 TPM_DEBUG_IO_WRITE(TIS_REG_DATA_FIFO, data);
Julius Werner2f37bd62015-02-19 14:51:15 -0800167 write8(TIS_REG(locality, TIS_REG_DATA_FIFO), data);
Aaron Durbinc4220022013-07-24 16:02:14 -0500168}
169
170static inline u16 tpm_read_burst_count(int locality)
171{
172 u16 count;
Julius Werner2f37bd62015-02-19 14:51:15 -0800173 count = read8(TIS_REG(locality, TIS_REG_BURST_COUNT));
174 count |= read8(TIS_REG(locality, TIS_REG_BURST_COUNT + 1)) << 8;
Aaron Durbinc4220022013-07-24 16:02:14 -0500175 TPM_DEBUG_IO_READ(TIS_REG_BURST_COUNT, count);
176 return count;
177}
178
179static inline u8 tpm_read_access(int locality)
180{
Julius Werner2f37bd62015-02-19 14:51:15 -0800181 u8 value = read8(TIS_REG(locality, TIS_REG_ACCESS));
Aaron Durbinc4220022013-07-24 16:02:14 -0500182 TPM_DEBUG_IO_READ(TIS_REG_ACCESS, value);
183 return value;
184}
185
186static inline void tpm_write_access(u8 data, int locality)
187{
188 TPM_DEBUG_IO_WRITE(TIS_REG_ACCESS, data);
Julius Werner2f37bd62015-02-19 14:51:15 -0800189 write8(TIS_REG(locality, TIS_REG_ACCESS), data);
Aaron Durbinc4220022013-07-24 16:02:14 -0500190}
191
Sergii Dmytruk76086992022-10-30 17:19:46 +0200192static inline u32 tpm_read_intf_cap(int locality)
193{
194 u32 value = read32(TIS_REG(locality, TIS_REG_INTF_CAPABILITY));
195 TPM_DEBUG_IO_READ(TIS_REG_INTF_CAPABILITY, value);
196 return value;
197}
198
199static inline u32 tpm_read_intf_id(int locality)
200{
201 u32 value = read32(TIS_REG(locality, TIS_REG_INTF_ID));
202 TPM_DEBUG_IO_READ(TIS_REG_INTF_ID, value);
203 return value;
204}
205
Aaron Durbinc4220022013-07-24 16:02:14 -0500206static inline u32 tpm_read_did_vid(int locality)
207{
Julius Werner2f37bd62015-02-19 14:51:15 -0800208 u32 value = read32(TIS_REG(locality, TIS_REG_DID_VID));
Aaron Durbinc4220022013-07-24 16:02:14 -0500209 TPM_DEBUG_IO_READ(TIS_REG_DID_VID, value);
210 return value;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700211}
212
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700213static inline void tpm_write_int_vector(int vector, int locality)
214{
215 TPM_DEBUG_IO_WRITE(TIS_REG_INT_VECTOR, vector);
Julius Werner2f37bd62015-02-19 14:51:15 -0800216 write8(TIS_REG(locality, TIS_REG_INT_VECTOR), vector & 0xf);
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700217}
218
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530219static inline u8 tpm_read_int_vector(int locality)
220{
221 u8 value = read8(TIS_REG(locality, TIS_REG_INT_VECTOR));
222 TPM_DEBUG_IO_READ(TIS_REG_INT_VECTOR, value);
223 return value;
224}
225
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700226static inline void tpm_write_int_polarity(int polarity, int locality)
227{
228 /* Set polarity and leave all other bits at 0 */
229 u32 value = (polarity & 0x3) << 3;
230 TPM_DEBUG_IO_WRITE(TIS_REG_INT_ENABLE, value);
Julius Werner2f37bd62015-02-19 14:51:15 -0800231 write32(TIS_REG(locality, TIS_REG_INT_ENABLE), value);
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700232}
233
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530234static inline u32 tpm_read_int_polarity(int locality)
235{
236 /* Get polarity and leave all other bits */
237 u32 value = read8(TIS_REG(locality, TIS_REG_INT_ENABLE));
238 value = (value >> 3) & 0x3;
239 TPM_DEBUG_IO_READ(TIS_REG_INT_ENABLE, value);
240 return value;
241}
242
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700243/*
Aaron Durbinc4220022013-07-24 16:02:14 -0500244 * tis_wait_sts()
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700245 *
Werner Zeh92ab6112021-10-11 15:27:12 +0200246 * Wait for at most a second for a status to change its state to match the
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700247 * expected state. Normally the transition happens within microseconds.
248 *
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700249 * @locality - locality
250 * @mask - bitmask for the bitfield(s) to watch
251 * @expected - value the field(s) are supposed to be set to
252 *
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600253 * Returns TPM_SUCCESS on success or TPM_CB_TIMEOUT on timeout.
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700254 */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600255static tpm_result_t tis_wait_sts(int locality, u8 mask, u8 expected)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700256{
Werner Zeh92ab6112021-10-11 15:27:12 +0200257 struct stopwatch sw;
258
259 stopwatch_init_usecs_expire(&sw, MAX_DELAY_US);
260 do {
Aaron Durbinc4220022013-07-24 16:02:14 -0500261 u8 value = tpm_read_status(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700262 if ((value & mask) == expected)
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600263 return TPM_SUCCESS;
Werner Zeh92ab6112021-10-11 15:27:12 +0200264 udelay(1);
265 } while (!stopwatch_expired(&sw));
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600266 return TPM_CB_TIMEOUT;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700267}
268
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600269static inline tpm_result_t tis_wait_ready(int locality)
Aaron Durbinc4220022013-07-24 16:02:14 -0500270{
271 return tis_wait_sts(locality, TIS_STS_COMMAND_READY,
272 TIS_STS_COMMAND_READY);
273}
274
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600275static inline tpm_result_t tis_wait_valid(int locality)
Aaron Durbinc4220022013-07-24 16:02:14 -0500276{
277 return tis_wait_sts(locality, TIS_STS_VALID, TIS_STS_VALID);
278}
279
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600280static inline tpm_result_t tis_wait_valid_data(int locality)
Aaron Durbinc4220022013-07-24 16:02:14 -0500281{
282 const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
283 return tis_wait_sts(locality, has_data, has_data);
284}
285
286static inline int tis_has_valid_data(int locality)
287{
288 const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
289 return (tpm_read_status(locality) & has_data) == has_data;
290}
291
292static inline int tis_expect_data(int locality)
293{
294 return !!(tpm_read_status(locality) & TIS_STS_EXPECT);
295}
296
297/*
298 * tis_wait_access()
299 *
Werner Zeh92ab6112021-10-11 15:27:12 +0200300 * Wait for at most a second for a access to change its state to match the
Aaron Durbinc4220022013-07-24 16:02:14 -0500301 * expected state. Normally the transition happens within microseconds.
302 *
303 * @locality - locality
304 * @mask - bitmask for the bitfield(s) to watch
305 * @expected - value the field(s) are supposed to be set to
306 *
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600307 * Returns TPM_SUCCESS on success or TPM_CB_TIMEOUT on timeout.
Aaron Durbinc4220022013-07-24 16:02:14 -0500308 */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600309static tpm_result_t tis_wait_access(int locality, u8 mask, u8 expected)
Aaron Durbinc4220022013-07-24 16:02:14 -0500310{
Werner Zeh92ab6112021-10-11 15:27:12 +0200311 struct stopwatch sw;
312
313 stopwatch_init_usecs_expire(&sw, MAX_DELAY_US);
314 do {
Aaron Durbinc4220022013-07-24 16:02:14 -0500315 u8 value = tpm_read_access(locality);
316 if ((value & mask) == expected)
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600317 return TPM_SUCCESS;
Werner Zeh92ab6112021-10-11 15:27:12 +0200318 udelay(1);
319 } while (!stopwatch_expired(&sw));
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600320 return TPM_CB_TIMEOUT;
Aaron Durbinc4220022013-07-24 16:02:14 -0500321}
322
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600323static inline tpm_result_t tis_wait_received_access(int locality)
Aaron Durbinc4220022013-07-24 16:02:14 -0500324{
325 return tis_wait_access(locality, TIS_ACCESS_ACTIVE_LOCALITY,
326 TIS_ACCESS_ACTIVE_LOCALITY);
327}
328
329static inline int tis_has_access(int locality)
330{
331 return !!(tpm_read_access(locality) & TIS_ACCESS_ACTIVE_LOCALITY);
332}
333
334static inline void tis_request_access(int locality)
335{
336 tpm_write_access(TIS_ACCESS_REQUEST_USE, locality);
337}
338
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700339/*
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700340 * PC Client Specific TPM Interface Specification section 11.2.12:
341 *
342 * Software must be prepared to send two writes of a "1" to command ready
343 * field: the first to indicate successful read of all the data, thus
344 * clearing the data from the ReadFIFO and freeing the TPM's resources,
345 * and the second to indicate to the TPM it is about to send a new command.
346 *
347 * In practice not all TPMs behave the same so it is necessary to be
348 * flexible when trying to set command ready.
349 *
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700350 */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600351static tpm_result_t tis_command_ready(u8 locality)
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700352{
353 u32 status;
354
355 /* 1st attempt to set command ready */
Aaron Durbinc4220022013-07-24 16:02:14 -0500356 tpm_write_status(TIS_STS_COMMAND_READY, locality);
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700357
358 /* Wait for response */
Aaron Durbinc4220022013-07-24 16:02:14 -0500359 status = tpm_read_status(locality);
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700360
361 /* Check if command ready is set yet */
362 if (status & TIS_STS_COMMAND_READY)
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600363 return TPM_SUCCESS;
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700364
365 /* 2nd attempt to set command ready */
Aaron Durbinc4220022013-07-24 16:02:14 -0500366 tpm_write_status(TIS_STS_COMMAND_READY, locality);
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700367
Aaron Durbinc4220022013-07-24 16:02:14 -0500368 return tis_wait_ready(locality);
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700369}
370
371/*
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300372 * pc80_tis_probe()
Jon Murphya7c64d72023-09-15 07:58:08 -0600373 *
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700374 * Probe the TPM device and try determining its manufacturer/device name.
375 *
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600376 * Returns TPM_SUCCESS on success (the device is found or was found during
377 * an earlier invocation) or TPM_CB_FAIL if the device is not found.
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700378 */
Sergii Dmytruk3e5cefc2022-11-01 00:48:43 +0200379static tpm_result_t pc80_tpm_probe(enum tpm_family *family)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700380{
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200381 static enum tpm_family tpm_family;
382
Sergii Dmytruk76086992022-10-30 17:19:46 +0200383 const char *device_name = NULL;
384 const char *vendor_name = NULL;
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700385 const struct device_name *dev;
Sergii Dmytruk76086992022-10-30 17:19:46 +0200386 u32 didvid, intf_id;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700387 u16 vid, did;
Sergii Dmytruk76086992022-10-30 17:19:46 +0200388 u8 locality = 0, intf_type;
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700389 int i;
Sergii Dmytruk76086992022-10-30 17:19:46 +0200390 const char *family_str;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700391
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200392 if (vendor_dev_id) {
393 if (family != NULL)
394 *family = tpm_family;
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600395 return TPM_SUCCESS; /* Already probed. */
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200396 }
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700397
Aaron Durbinc4220022013-07-24 16:02:14 -0500398 didvid = tpm_read_did_vid(0);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700399 if (!didvid || (didvid == 0xffffffff)) {
Angel Pons08e8cab2020-06-18 15:20:37 +0200400 printf("%s: No TPM device found\n", __func__);
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600401 return TPM_CB_FAIL;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700402 }
403
Sergii Dmytruk76086992022-10-30 17:19:46 +0200404 intf_id = tpm_read_intf_id(locality);
405 intf_type = (intf_id & 0xf);
406 if (intf_type == 0xf) {
407 u32 intf_cap = tpm_read_intf_cap(locality);
408 u8 intf_version = (intf_cap >> 28) & 0x7;
409 switch (intf_version) {
410 case 0:
411 case 2:
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200412 tpm_family = TPM_1;
Sergii Dmytruk76086992022-10-30 17:19:46 +0200413 break;
414 case 3:
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200415 tpm_family = TPM_2;
Sergii Dmytruk76086992022-10-30 17:19:46 +0200416 break;
417 default:
418 printf("%s: Unexpected TPM interface version: %d\n", __func__,
419 intf_version);
420 return TPM_CB_PROBE_FAILURE;
421 }
422 } else if (intf_type == 0) {
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200423 tpm_family = TPM_2;
Sergii Dmytruk76086992022-10-30 17:19:46 +0200424 } else {
425 printf("%s: Unexpected TPM interface type: %d\n", __func__, intf_type);
426 return TPM_CB_PROBE_FAILURE;
427 }
428
Arthur Heymans0ca944b2019-11-20 19:51:06 +0100429 vendor_dev_id = didvid;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700430
431 vid = didvid & 0xffff;
432 did = (didvid >> 16) & 0xffff;
433 for (i = 0; i < ARRAY_SIZE(vendor_names); i++) {
434 int j = 0;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700435 if (vid == vendor_names[i].vendor_id) {
436 vendor_name = vendor_names[i].vendor_name;
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700437 } else {
438 continue;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700439 }
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700440 dev = &vendor_names[i].dev_names[j];
Sergii Dmytruk76086992022-10-30 17:19:46 +0200441 while (dev->dev_id != 0xffff) {
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200442 if (dev->dev_id == did && dev->family == tpm_family) {
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700443 device_name = dev->dev_name;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700444 break;
445 }
446 j++;
Subrata41b08d92015-05-14 14:38:07 +0530447 dev = &vendor_names[i].dev_names[j];
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700448 }
449 break;
450 }
Sergii Dmytruk76086992022-10-30 17:19:46 +0200451
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200452 family_str = (tpm_family == TPM_1 ? "TPM 1.2" : "TPM 2.0");
Sergii Dmytruk76086992022-10-30 17:19:46 +0200453 if (vendor_name == NULL) {
454 printk(BIOS_INFO, "Found %s 0x%04x by 0x%04x\n", family_str, did, vid);
455 } else if (device_name == NULL) {
456 printk(BIOS_INFO, "Found %s 0x%04x by %s (0x%04x)\n", family_str, did,
457 vendor_name, vid);
458 } else {
459 printk(BIOS_INFO, "Found %s %s (0x%04x) by %s (0x%04x)\n", family_str,
460 device_name, did, vendor_name, vid);
461 }
462
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200463 if (family != NULL)
464 *family = tpm_family;
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600465 return TPM_SUCCESS;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700466}
467
468/*
469 * tis_senddata()
470 *
471 * send the passed in data to the TPM device.
472 *
473 * @data - address of the data to send, byte by byte
474 * @len - length of the data to send
475 *
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600476 * Returns TPM_SUCCESS on success, TPM_CB_FAIL on error (in case the device does
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700477 * not accept the entire command).
478 */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600479static tpm_result_t tis_senddata(const u8 *const data, u32 len)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700480{
481 u32 offset = 0;
482 u16 burst = 0;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700483 u8 locality = 0;
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600484 tpm_result_t rc = TPM_SUCCESS;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700485
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600486 rc = tis_wait_ready(locality);
487 if (rc) {
488 printf("%s:%d - failed to get 'command_ready' status with error %#x\n",
489 __FILE__, __LINE__, rc);
490 return rc;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700491 }
Aaron Durbinc4220022013-07-24 16:02:14 -0500492 burst = tpm_read_burst_count(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700493
494 while (1) {
Martin Roth38ddbfb2019-10-23 21:41:00 -0600495 unsigned int count;
Werner Zeh92ab6112021-10-11 15:27:12 +0200496 struct stopwatch sw;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700497
498 /* Wait till the device is ready to accept more data. */
Werner Zeh92ab6112021-10-11 15:27:12 +0200499 stopwatch_init_usecs_expire(&sw, MAX_DELAY_US);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700500 while (!burst) {
Werner Zeh92ab6112021-10-11 15:27:12 +0200501 if (stopwatch_expired(&sw)) {
Werner Zeh66b2f202021-10-12 15:14:22 +0200502 printf("%s:%d failed to feed %u bytes of %u\n",
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700503 __FILE__, __LINE__, len - offset, len);
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600504 return TPM_CB_TIMEOUT;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700505 }
506 udelay(1);
Aaron Durbinc4220022013-07-24 16:02:14 -0500507 burst = tpm_read_burst_count(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700508 }
509
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700510 /*
511 * Calculate number of bytes the TPM is ready to accept in one
512 * shot.
513 *
514 * We want to send the last byte outside of the loop (hence
515 * the -1 below) to make sure that the 'expected' status bit
516 * changes to zero exactly after the last byte is fed into the
517 * FIFO.
518 */
Elyes HAOUAS361a9352019-12-18 21:26:33 +0100519 count = MIN(burst, len - offset - 1);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700520 while (count--)
Aaron Durbinc4220022013-07-24 16:02:14 -0500521 tpm_write_data(data[offset++], locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700522
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600523 rc = tis_wait_valid(locality);
524 if (rc || !tis_expect_data(locality)) {
525 printf("%s:%d TPM command feed overflow with error %#x\n",
526 __FILE__, __LINE__, rc);
527 return rc ? rc : TPM_CB_FAIL;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700528 }
529
Aaron Durbinc4220022013-07-24 16:02:14 -0500530 burst = tpm_read_burst_count(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700531 if ((offset == (len - 1)) && burst)
532 /*
533 * We need to be able to send the last byte to the
534 * device, so burst size must be nonzero before we
535 * break out.
536 */
537 break;
538 }
539
540 /* Send the last byte. */
Aaron Durbinc4220022013-07-24 16:02:14 -0500541 tpm_write_data(data[offset++], locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700542
543 /*
544 * Verify that TPM does not expect any more data as part of this
545 * command.
546 */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600547 rc = tis_wait_valid(locality);
548 if (rc || tis_expect_data(locality)) {
549 printf("%s:%d unexpected TPM error %#x with status %#x\n",
550 __FILE__, __LINE__, rc, tpm_read_status(locality));
551 return rc ? rc : TPM_CB_FAIL;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700552 }
553
554 /* OK, sitting pretty, let's start the command execution. */
Aaron Durbinc4220022013-07-24 16:02:14 -0500555 tpm_write_status(TIS_STS_TPM_GO, locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700556
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600557 return TPM_SUCCESS;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700558}
559
560/*
561 * tis_readresponse()
562 *
563 * read the TPM device response after a command was issued.
564 *
565 * @buffer - address where to read the response, byte by byte.
566 * @len - pointer to the size of buffer
567 *
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600568 * On success stores the number of received bytes to len and returns
569 * TPM_SUCCESS. On errors (misformatted TPM data or synchronization
570 * problems) returns TPM_CB_FAIL.
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700571 */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600572static tpm_result_t tis_readresponse(u8 *buffer, size_t *len)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700573{
574 u16 burst_count;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700575 u32 offset = 0;
576 u8 locality = 0;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700577 u32 expected_count = *len;
578 int max_cycles = 0;
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600579 tpm_result_t rc = TPM_SUCCESS;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700580
581 /* Wait for the TPM to process the command */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600582 rc = tis_wait_valid_data(locality);
583 if (rc) {
584 printf("%s:%d failed processing command with error %#x\n",
585 __FILE__, __LINE__, rc);
586 return rc;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700587 }
588
589 do {
Aaron Durbinc4220022013-07-24 16:02:14 -0500590 while ((burst_count = tpm_read_burst_count(locality)) == 0) {
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700591 if (max_cycles++ == MAX_DELAY_US) {
592 printf("%s:%d TPM stuck on read\n",
593 __FILE__, __LINE__);
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600594 return TPM_CB_FAIL;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700595 }
596 udelay(1);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700597 }
598
599 max_cycles = 0;
600
601 while (burst_count-- && (offset < expected_count)) {
Aaron Durbinc4220022013-07-24 16:02:14 -0500602 buffer[offset++] = tpm_read_data(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700603 if (offset == 6) {
604 /*
605 * We got the first six bytes of the reply,
606 * let's figure out how many bytes to expect
607 * total - it is stored as a 4 byte number in
608 * network order, starting with offset 2 into
609 * the body of the reply.
610 */
611 u32 real_length;
612 memcpy(&real_length,
613 buffer + 2,
614 sizeof(real_length));
615 expected_count = be32_to_cpu(real_length);
616
617 if ((expected_count < offset) ||
618 (expected_count > *len)) {
Werner Zeh66b2f202021-10-12 15:14:22 +0200619 printf("%s:%d bad response size %u\n",
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700620 __FILE__, __LINE__,
621 expected_count);
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600622 return TPM_CB_FAIL;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700623 }
624 }
625 }
626
627 /* Wait for the next portion */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600628 rc = tis_wait_valid(locality);
629 if (rc) {
630 printf("%s:%d failed to read response with error %#x\n",
631 __FILE__, __LINE__, rc);
632 return rc;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700633 }
634
635 if (offset == expected_count)
636 break; /* We got all we need */
637
Bill XIEa4bf0b72018-03-22 17:07:43 +0800638 /*
639 * Certain TPMs seem to need some delay between tis_wait_valid()
640 * and tis_has_valid_data(), or some race-condition-related
641 * issue will occur.
642 */
Julius Wernercd49cce2019-03-05 16:53:33 -0800643 if (CONFIG(TPM_RDRESP_NEED_DELAY))
Bill XIEa4bf0b72018-03-22 17:07:43 +0800644 udelay(10);
645
Aaron Durbinc4220022013-07-24 16:02:14 -0500646 } while (tis_has_valid_data(locality));
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700647
Aaron Durbinc4220022013-07-24 16:02:14 -0500648 /* * Make sure we indeed read all there was. */
649 if (tis_has_valid_data(locality)) {
Jon Murphy53fc6672023-09-26 21:05:37 -0600650 printf("%s:%d wrong receive status: %#x %u bytes left\n",
Aaron Durbinc4220022013-07-24 16:02:14 -0500651 __FILE__, __LINE__, tpm_read_status(locality),
652 tpm_read_burst_count(locality));
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600653 return TPM_CB_FAIL;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700654 }
655
656 /* Tell the TPM that we are done. */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600657 rc = tis_command_ready(locality);
658 if (rc)
659 return rc;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700660
661 *len = offset;
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600662 return TPM_SUCCESS;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700663}
664
665/*
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300666 * pc80_tis_open()
Sergii Dmytruk4ee03172022-12-22 19:35:25 +0200667 *
Sergii Dmytruk0a89d522022-10-29 16:57:07 +0300668 * Requests access to locality 0 for the caller.
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700669 *
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600670 * Returns TPM_SUCCESS on success, TSS Error on failure.
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700671 */
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300672static tpm_result_t pc80_tis_open(void)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700673{
674 u8 locality = 0; /* we use locality zero for everything */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600675 tpm_result_t rc = TPM_SUCCESS;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700676
Sergii Dmytruk0a89d522022-10-29 16:57:07 +0300677 if (!tis_has_access(locality)) {
678 /* request access to locality */
679 tis_request_access(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700680
Sergii Dmytruk0a89d522022-10-29 16:57:07 +0300681 /* did we get a lock? */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600682 rc = tis_wait_received_access(locality);
683 if (rc) {
684 printf("%s:%d - failed to lock locality %u with error %#x\n",
685 __FILE__, __LINE__, locality, rc);
686 return rc;
Sergii Dmytruk0a89d522022-10-29 16:57:07 +0300687 }
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700688
Sergii Dmytruk0a89d522022-10-29 16:57:07 +0300689 /* Certain TPMs seem to need some delay here or they hang... */
690 udelay(10);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700691 }
692
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600693 return tis_command_ready(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700694}
695
696/*
Sergii Dmytruk4ee03172022-12-22 19:35:25 +0200697 * tis_sendrecv()
698 *
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700699 * Send the requested data to the TPM and then try to get its response
700 *
701 * @sendbuf - buffer of the data to send
702 * @send_size size of the data to send
703 * @recvbuf - memory to save the response to
704 * @recv_len - pointer to the size of the response buffer
705 *
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600706 * Returns TPM_SUCCESS on success (and places the number of response bytes
707 * at recv_len) or TPM_CB_FAIL on failure.
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700708 */
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300709static tpm_result_t pc80_tpm_sendrecv(const uint8_t *sendbuf, size_t send_size,
710 uint8_t *recvbuf, size_t *recv_len)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700711{
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600712 tpm_result_t rc = tis_senddata(sendbuf, send_size);
713 if (rc) {
714 printf("%s:%d failed sending data to TPM with error %#x\n",
715 __FILE__, __LINE__, rc);
716 return rc;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700717 }
718
719 return tis_readresponse(recvbuf, recv_len);
720}
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700721
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700722/*
Sergii Dmytruk3e5cefc2022-11-01 00:48:43 +0200723 * pc80_tis_probe()
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300724 *
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200725 * Probe for the TPM device and set it up for use within locality 0.
726 *
727 * @tpm_family - pointer to tpm_family which is set to TPM family of the device.
728 *
729 * Returns pointer to send-receive function on success or NULL on failure.
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300730 */
Sergii Dmytruk3e5cefc2022-11-01 00:48:43 +0200731tis_sendrecv_fn pc80_tis_probe(enum tpm_family *family)
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300732{
Sergii Dmytruk3e5cefc2022-11-01 00:48:43 +0200733 if (pc80_tpm_probe(family))
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300734 return NULL;
735
736 if (pc80_tis_open())
737 return NULL;
738
739 return &pc80_tpm_sendrecv;
740}
741
742/*
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700743 * tis_setup_interrupt()
744 *
745 * Set up the interrupt vector and polarity for locality 0 and
746 * disable all interrupts so they are unused in firmware but can
747 * be enabled by the OS.
748 *
749 * The values used here must match what is passed in the TPM ACPI
750 * device if ACPI is used on the platform.
751 *
752 * @vector - TPM interrupt vector
753 * @polarity - TPM interrupt polarity
754 *
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600755 * Returns TPM_SUCCESS on success, TPM_CB_FAIL on failure.
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700756 */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600757static tpm_result_t tis_setup_interrupt(int vector, int polarity)
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700758{
759 u8 locality = 0;
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600760 tpm_result_t rc = tlcl_lib_init();
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700761
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600762 if (rc)
763 return rc;
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700764
765 /* Set TPM interrupt vector */
766 tpm_write_int_vector(vector, locality);
767
Elyes HAOUAS18958382018-08-07 12:23:16 +0200768 /* Set TPM interrupt polarity and disable interrupts */
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700769 tpm_write_int_polarity(polarity, locality);
770
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600771 return TPM_SUCCESS;
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700772}
773
774static void lpc_tpm_read_resources(struct device *dev)
775{
776 /* Static 5K memory region specified in Kconfig */
Arthur Heymanse05693e2023-07-05 11:43:09 +0200777 mmio_range(dev, 0, CONFIG_TPM_TIS_BASE_ADDRESS, 0x5000);
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700778}
779
780static void lpc_tpm_set_resources(struct device *dev)
781{
782 tpm_config_t *config = (tpm_config_t *)dev->chip_info;
Kyösti Mälkkibaa16e92019-11-05 18:38:00 +0200783 DEVTREE_CONST struct resource *res;
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700784
785 for (res = dev->resource_list; res; res = res->next) {
786 if (!(res->flags & IORESOURCE_ASSIGNED))
787 continue;
788
789 if (res->flags & IORESOURCE_IRQ) {
790 /* Set interrupt vector */
791 tis_setup_interrupt((int)res->base,
792 config->irq_polarity);
793 } else {
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700794 continue;
795 }
796
Kyösti Mälkkibaa16e92019-11-05 18:38:00 +0200797#if !DEVTREE_EARLY
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700798 res->flags |= IORESOURCE_STORED;
799 report_resource_stored(dev, res, " <tpm>");
Kyösti Mälkkibaa16e92019-11-05 18:38:00 +0200800#endif
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700801 }
802}
803
Julius Wernercd49cce2019-03-05 16:53:33 -0800804#if CONFIG(HAVE_ACPI_TABLES)
Furquan Shaikh7536a392020-04-24 21:59:21 -0700805static void lpc_tpm_fill_ssdt(const struct device *dev)
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530806{
Michał Kopeća691cbd2022-02-22 12:30:22 +0100807 /* Windows 11 requires the following path for TPM to be detected */
808 const char *path = "\\_SB_.PCI0";
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530809
810 /* Device */
811 acpigen_write_scope(path);
812 acpigen_write_device(acpi_device_name(dev));
813
Sergii Dmytruk47e9e8c2022-11-02 00:50:03 +0200814 if (tlcl_get_family() == TPM_2) {
Michał Żygowski7b288012020-03-20 15:41:44 +0100815 acpigen_write_name_string("_HID", "MSFT0101");
816 acpigen_write_name_string("_CID", "MSFT0101");
817 } else {
818 acpigen_write_name("_HID");
819 acpigen_emit_eisaid("PNP0C31");
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530820
Michał Żygowski7b288012020-03-20 15:41:44 +0100821 acpigen_write_name("_CID");
822 acpigen_emit_eisaid("PNP0C31");
823 }
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530824
Patrick Rudolphc83bab62019-12-13 12:16:06 +0100825 acpi_device_write_uid(dev);
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530826
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530827 u32 did_vid = tpm_read_did_vid(0);
828 if (did_vid > 0 && did_vid < 0xffffffff)
829 acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON);
830 else
831 acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_OFF);
832
Kevin Cody-Littlec97b5af2018-05-09 14:14:59 -0400833 u16 port = dev->path.pnp.port;
834
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530835 /* Resources */
836 acpigen_write_name("_CRS");
837 acpigen_write_resourcetemplate_header();
838 acpigen_write_mem32fixed(1, CONFIG_TPM_TIS_BASE_ADDRESS, 0x5000);
Frans Hendriks6cc937e2018-10-29 14:30:58 +0100839 if (port)
840 acpigen_write_io16(port, port, 1, 2, 1);
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530841
842 if (CONFIG_TPM_PIRQ) {
843 /*
844 * PIRQ: Update interrupt vector with configured PIRQ
845 * Active-Low Level-Triggered Shared
846 */
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800847 struct acpi_irq tpm_irq_a = ACPI_IRQ_LEVEL_LOW(CONFIG_TPM_PIRQ);
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530848 acpi_device_write_interrupt(&tpm_irq_a);
849 } else if (tpm_read_int_vector(0) > 0) {
850 u8 int_vec = tpm_read_int_vector(0);
851 u8 int_pol = tpm_read_int_polarity(0);
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800852 struct acpi_irq tpm_irq = ACPI_IRQ_LEVEL_LOW(int_vec);
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530853
854 if (int_pol & 1)
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800855 tpm_irq.polarity = ACPI_IRQ_ACTIVE_LOW;
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530856 else
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800857 tpm_irq.polarity = ACPI_IRQ_ACTIVE_HIGH;
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530858
859 if (int_pol & 2)
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800860 tpm_irq.mode = ACPI_IRQ_EDGE_TRIGGERED;
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530861 else
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800862 tpm_irq.mode = ACPI_IRQ_LEVEL_TRIGGERED;
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530863
864 acpi_device_write_interrupt(&tpm_irq);
865 }
866
Patrick Rudolphd8d8be12020-09-21 09:48:53 +0200867
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530868 acpigen_write_resourcetemplate_footer();
869
Patrick Rudolphd8d8be12020-09-21 09:48:53 +0200870 if (!CONFIG(CHROMEOS))
871 tpm_ppi_acpi_fill_ssdt(dev);
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530872
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530873 acpigen_pop_len(); /* Device */
874 acpigen_pop_len(); /* Scope */
875
Kyösti Mälkkibaa16e92019-11-05 18:38:00 +0200876#if !DEVTREE_EARLY
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530877 printk(BIOS_INFO, "%s.%s: %s %s\n", path, acpi_device_name(dev),
878 dev->chip_ops->name, dev_path(dev));
Kyösti Mälkkibaa16e92019-11-05 18:38:00 +0200879#endif
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530880}
881
Aaron Durbinaa090cb2017-09-13 16:01:52 -0600882static const char *lpc_tpm_acpi_name(const struct device *dev)
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530883{
884 return "TPM";
885}
886#endif
887
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700888static struct device_operations lpc_tpm_ops = {
Elyes HAOUAS2aa3b162018-11-27 17:02:10 +0100889 .read_resources = lpc_tpm_read_resources,
890 .set_resources = lpc_tpm_set_resources,
Julius Wernercd49cce2019-03-05 16:53:33 -0800891#if CONFIG(HAVE_ACPI_TABLES)
Nico Huber68680dd2020-03-31 17:34:52 +0200892 .acpi_name = lpc_tpm_acpi_name,
893 .acpi_fill_ssdt = lpc_tpm_fill_ssdt,
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530894#endif
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700895};
896
Arthur Heymans04c49a52023-08-22 13:16:40 +0200897static struct device_operations noop_tpm_ops = {
898 .read_resources = noop_read_resources,
899 .set_resources = noop_set_resources,
900};
901
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700902static struct pnp_info pnp_dev_info[] = {
903 { .flags = PNP_IRQ0 }
904};
905
906static void enable_dev(struct device *dev)
907{
Kyösti Mälkkid2b2a182021-04-29 15:33:07 +0300908 if (CONFIG(TPM))
Kyösti Mälkki9cc64932020-05-29 19:42:07 +0300909 pnp_enable_devices(dev, &lpc_tpm_ops,
910 ARRAY_SIZE(pnp_dev_info), pnp_dev_info);
Arthur Heymans04c49a52023-08-22 13:16:40 +0200911 else
912 pnp_enable_devices(dev, &noop_tpm_ops, ARRAY_SIZE(pnp_dev_info), pnp_dev_info);
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700913}
914
915struct chip_operations drivers_pc80_tpm_ops = {
Nicholas Sudsgaardbfb11be2024-01-30 09:53:46 +0900916 .name = "LPC TPM",
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700917 .enable_dev = enable_dev
918};