blob: 06f543498f8724831dec2e97e55085a24c8f0ab5 [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>
Duncan Lauriedd281ed2014-10-30 15:20:19 -070023#include <device/pnp.h>
Patrick Rudolphd8d8be12020-09-21 09:48:53 +020024#include <drivers/tpm/tpm_ppi.h>
Werner Zeh92ab6112021-10-11 15:27:12 +020025#include <timer.h>
Duncan Lauriedd281ed2014-10-30 15:20:19 -070026#include "chip.h"
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070027
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070028#define PREFIX "lpc_tpm: "
Patrick Rudolphd8d8be12020-09-21 09:48:53 +020029
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070030/* coreboot wrapper for TPM driver (start) */
31#define TPM_DEBUG(fmt, args...) \
Julius Wernercd49cce2019-03-05 16:53:33 -080032 if (CONFIG(DEBUG_TPM)) { \
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070033 printk(BIOS_DEBUG, PREFIX); \
Elyes HAOUASa342f392018-10-17 10:56:26 +020034 printk(BIOS_DEBUG, fmt, ##args); \
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070035 }
Aaron Durbinc4220022013-07-24 16:02:14 -050036#define TPM_DEBUG_IO_READ(reg_, val_) \
37 TPM_DEBUG("Read reg 0x%x returns 0x%x\n", (reg_), (val_))
38#define TPM_DEBUG_IO_WRITE(reg_, val_) \
39 TPM_DEBUG("Write reg 0x%x with 0x%x\n", (reg_), (val_))
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070040#define printf(x...) printk(BIOS_ERR, x)
41
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070042/* coreboot wrapper for TPM driver (end) */
43
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070044/* the macro accepts the locality value, but only locality 0 is operational */
45#define TIS_REG(LOCALITY, REG) \
Patrick Rudolph56fdafb2020-07-01 20:07:08 +020046 (void *)(uintptr_t)(CONFIG_TPM_TIS_BASE_ADDRESS + (LOCALITY << 12) + REG)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070047
48/* hardware registers' offsets */
49#define TIS_REG_ACCESS 0x0
50#define TIS_REG_INT_ENABLE 0x8
51#define TIS_REG_INT_VECTOR 0xc
52#define TIS_REG_INT_STATUS 0x10
53#define TIS_REG_INTF_CAPABILITY 0x14
54#define TIS_REG_STS 0x18
Aaron Durbinc4220022013-07-24 16:02:14 -050055#define TIS_REG_BURST_COUNT 0x19
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070056#define TIS_REG_DATA_FIFO 0x24
57#define TIS_REG_DID_VID 0xf00
58#define TIS_REG_RID 0xf04
59
60/* Some registers' bit field definitions */
61#define TIS_STS_VALID (1 << 7) /* 0x80 */
62#define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */
63#define TIS_STS_TPM_GO (1 << 5) /* 0x20 */
64#define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */
65#define TIS_STS_EXPECT (1 << 3) /* 0x08 */
66#define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */
67
68#define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */
69#define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */
70#define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */
71#define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */
72#define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */
73#define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */
74#define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */
75
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070076/*
77 * Error value returned if a tpm register does not enter the expected state
78 * after continuous polling. No actual TPM register reading ever returns ~0,
79 * so this value is a safe error indication to be mixed with possible status
80 * register values.
81 */
82#define TPM_TIMEOUT_ERR (~0)
83
84/* Error value returned on various TPM driver errors */
85#define TPM_DRIVER_ERR (~0)
86
87 /* 1 second is plenty for anything TPM does.*/
Werner Zeh92ab6112021-10-11 15:27:12 +020088#define MAX_DELAY_US USECS_PER_SEC
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070089
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070090/*
91 * Structures defined below allow creating descriptions of TPM vendor/device
92 * ID information for run time discovery. The only device the system knows
93 * about at this time is Infineon slb9635
94 */
95struct device_name {
96 u16 dev_id;
Elyes HAOUASb0b0c8c2018-07-08 12:33:47 +020097 const char *const dev_name;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -070098};
99
100struct vendor_name {
101 u16 vendor_id;
Elyes HAOUASb0b0c8c2018-07-08 12:33:47 +0200102 const char *vendor_name;
103 const struct device_name *dev_names;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700104};
105
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700106static const struct device_name atmel_devices[] = {
107 {0x3204, "AT97SC3204"},
108 {0xffff}
109};
110
Stefan Reinauerc668af72011-10-27 21:28:25 +0000111static const struct device_name infineon_devices[] = {
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700112 {0x000b, "SLB9635 TT 1.2"},
Julius Wernercd49cce2019-03-05 16:53:33 -0800113#if CONFIG(TPM2)
Kamil Wcislobf5ccfd2017-10-12 13:12:11 +0200114 {0x001a, "SLB9665 TT 2.0"},
Subrata Banik5b8c4a72016-11-11 09:28:45 +0530115 {0x001b, "SLB9670 TT 2.0"},
116#else
Kamil Wcislobf5ccfd2017-10-12 13:12:11 +0200117 {0x001a, "SLB9660 TT 1.2"},
Wenkai Du641529b2015-05-29 10:54:27 -0700118 {0x001b, "SLB9670 TT 1.2"},
Subrata Banik5b8c4a72016-11-11 09:28:45 +0530119#endif
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700120 {0xffff}
121};
122
123static const struct device_name nuvoton_devices[] = {
124 {0x00fe, "NPCT420AA V2"},
125 {0xffff}
126};
127
128static const struct device_name stmicro_devices[] = {
129 {0x0000, "ST33ZP24" },
130 {0xffff}
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700131};
132
Tsung Ho Wu804a0432019-06-07 15:03:49 -0700133static const struct device_name swtpm_devices[] = {
134#if CONFIG(TPM2)
135 {0x0001, "SwTPM 2.0" },
136#endif
137 {0xffff}
138};
139
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700140static const struct vendor_name vendor_names[] = {
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700141 {0x1114, "Atmel", atmel_devices},
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700142 {0x15d1, "Infineon", infineon_devices},
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700143 {0x1050, "Nuvoton", nuvoton_devices},
Tsung Ho Wu804a0432019-06-07 15:03:49 -0700144 {0x1014, "TPM Emulator", swtpm_devices},
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700145 {0x104a, "ST Microelectronics", stmicro_devices},
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700146};
147
148/*
149 * Cached vendor/device ID pair to indicate that the device has been already
150 * discovered
151 */
Arthur Heymans0ca944b2019-11-20 19:51:06 +0100152static u32 vendor_dev_id;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700153
Aaron Durbinc4220022013-07-24 16:02:14 -0500154static inline u8 tpm_read_status(int locality)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700155{
Julius Werner2f37bd62015-02-19 14:51:15 -0800156 u8 value = read8(TIS_REG(locality, TIS_REG_STS));
Aaron Durbinc4220022013-07-24 16:02:14 -0500157 TPM_DEBUG_IO_READ(TIS_REG_STS, value);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700158 return value;
159}
160
Aaron Durbinc4220022013-07-24 16:02:14 -0500161static inline void tpm_write_status(u8 sts, int locality)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700162{
Aaron Durbinc4220022013-07-24 16:02:14 -0500163 TPM_DEBUG_IO_WRITE(TIS_REG_STS, sts);
Julius Werner2f37bd62015-02-19 14:51:15 -0800164 write8(TIS_REG(locality, TIS_REG_STS), sts);
Aaron Durbinc4220022013-07-24 16:02:14 -0500165}
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700166
Aaron Durbinc4220022013-07-24 16:02:14 -0500167static inline u8 tpm_read_data(int locality)
168{
Julius Werner2f37bd62015-02-19 14:51:15 -0800169 u8 value = read8(TIS_REG(locality, TIS_REG_DATA_FIFO));
Aaron Durbinc4220022013-07-24 16:02:14 -0500170 TPM_DEBUG_IO_READ(TIS_REG_DATA_FIFO, value);
171 return value;
172}
173
174static inline void tpm_write_data(u8 data, int locality)
175{
Werner Zeha31d6cd2021-10-07 07:15:38 +0200176 TPM_DEBUG_IO_WRITE(TIS_REG_DATA_FIFO, data);
Julius Werner2f37bd62015-02-19 14:51:15 -0800177 write8(TIS_REG(locality, TIS_REG_DATA_FIFO), data);
Aaron Durbinc4220022013-07-24 16:02:14 -0500178}
179
180static inline u16 tpm_read_burst_count(int locality)
181{
182 u16 count;
Julius Werner2f37bd62015-02-19 14:51:15 -0800183 count = read8(TIS_REG(locality, TIS_REG_BURST_COUNT));
184 count |= read8(TIS_REG(locality, TIS_REG_BURST_COUNT + 1)) << 8;
Aaron Durbinc4220022013-07-24 16:02:14 -0500185 TPM_DEBUG_IO_READ(TIS_REG_BURST_COUNT, count);
186 return count;
187}
188
189static inline u8 tpm_read_access(int locality)
190{
Julius Werner2f37bd62015-02-19 14:51:15 -0800191 u8 value = read8(TIS_REG(locality, TIS_REG_ACCESS));
Aaron Durbinc4220022013-07-24 16:02:14 -0500192 TPM_DEBUG_IO_READ(TIS_REG_ACCESS, value);
193 return value;
194}
195
196static inline void tpm_write_access(u8 data, int locality)
197{
198 TPM_DEBUG_IO_WRITE(TIS_REG_ACCESS, data);
Julius Werner2f37bd62015-02-19 14:51:15 -0800199 write8(TIS_REG(locality, TIS_REG_ACCESS), data);
Aaron Durbinc4220022013-07-24 16:02:14 -0500200}
201
202static inline u32 tpm_read_did_vid(int locality)
203{
Julius Werner2f37bd62015-02-19 14:51:15 -0800204 u32 value = read32(TIS_REG(locality, TIS_REG_DID_VID));
Aaron Durbinc4220022013-07-24 16:02:14 -0500205 TPM_DEBUG_IO_READ(TIS_REG_DID_VID, value);
206 return value;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700207}
208
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700209static inline void tpm_write_int_vector(int vector, int locality)
210{
211 TPM_DEBUG_IO_WRITE(TIS_REG_INT_VECTOR, vector);
Julius Werner2f37bd62015-02-19 14:51:15 -0800212 write8(TIS_REG(locality, TIS_REG_INT_VECTOR), vector & 0xf);
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700213}
214
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530215static inline u8 tpm_read_int_vector(int locality)
216{
217 u8 value = read8(TIS_REG(locality, TIS_REG_INT_VECTOR));
218 TPM_DEBUG_IO_READ(TIS_REG_INT_VECTOR, value);
219 return value;
220}
221
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700222static inline void tpm_write_int_polarity(int polarity, int locality)
223{
224 /* Set polarity and leave all other bits at 0 */
225 u32 value = (polarity & 0x3) << 3;
226 TPM_DEBUG_IO_WRITE(TIS_REG_INT_ENABLE, value);
Julius Werner2f37bd62015-02-19 14:51:15 -0800227 write32(TIS_REG(locality, TIS_REG_INT_ENABLE), value);
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700228}
229
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530230static inline u32 tpm_read_int_polarity(int locality)
231{
232 /* Get polarity and leave all other bits */
233 u32 value = read8(TIS_REG(locality, TIS_REG_INT_ENABLE));
234 value = (value >> 3) & 0x3;
235 TPM_DEBUG_IO_READ(TIS_REG_INT_ENABLE, value);
236 return value;
237}
238
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700239/*
Aaron Durbinc4220022013-07-24 16:02:14 -0500240 * tis_wait_sts()
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700241 *
Werner Zeh92ab6112021-10-11 15:27:12 +0200242 * Wait for at most a second for a status to change its state to match the
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700243 * expected state. Normally the transition happens within microseconds.
244 *
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700245 * @locality - locality
246 * @mask - bitmask for the bitfield(s) to watch
247 * @expected - value the field(s) are supposed to be set to
248 *
Aaron Durbinc4220022013-07-24 16:02:14 -0500249 * Returns 0 on success or TPM_TIMEOUT_ERR on timeout.
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700250 */
Aaron Durbinc4220022013-07-24 16:02:14 -0500251static int tis_wait_sts(int locality, u8 mask, u8 expected)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700252{
Werner Zeh92ab6112021-10-11 15:27:12 +0200253 struct stopwatch sw;
254
255 stopwatch_init_usecs_expire(&sw, MAX_DELAY_US);
256 do {
Aaron Durbinc4220022013-07-24 16:02:14 -0500257 u8 value = tpm_read_status(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700258 if ((value & mask) == expected)
Aaron Durbinc4220022013-07-24 16:02:14 -0500259 return 0;
Werner Zeh92ab6112021-10-11 15:27:12 +0200260 udelay(1);
261 } while (!stopwatch_expired(&sw));
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700262 return TPM_TIMEOUT_ERR;
263}
264
Aaron Durbinc4220022013-07-24 16:02:14 -0500265static inline int tis_wait_ready(int locality)
266{
267 return tis_wait_sts(locality, TIS_STS_COMMAND_READY,
268 TIS_STS_COMMAND_READY);
269}
270
271static inline int tis_wait_valid(int locality)
272{
273 return tis_wait_sts(locality, TIS_STS_VALID, TIS_STS_VALID);
274}
275
276static inline int tis_wait_valid_data(int locality)
277{
278 const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
279 return tis_wait_sts(locality, has_data, has_data);
280}
281
282static inline int tis_has_valid_data(int locality)
283{
284 const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
285 return (tpm_read_status(locality) & has_data) == has_data;
286}
287
288static inline int tis_expect_data(int locality)
289{
290 return !!(tpm_read_status(locality) & TIS_STS_EXPECT);
291}
292
293/*
294 * tis_wait_access()
295 *
Werner Zeh92ab6112021-10-11 15:27:12 +0200296 * Wait for at most a second for a access to change its state to match the
Aaron Durbinc4220022013-07-24 16:02:14 -0500297 * expected state. Normally the transition happens within microseconds.
298 *
299 * @locality - locality
300 * @mask - bitmask for the bitfield(s) to watch
301 * @expected - value the field(s) are supposed to be set to
302 *
303 * Returns 0 on success or TPM_TIMEOUT_ERR on timeout.
304 */
305static int tis_wait_access(int locality, u8 mask, u8 expected)
306{
Werner Zeh92ab6112021-10-11 15:27:12 +0200307 struct stopwatch sw;
308
309 stopwatch_init_usecs_expire(&sw, MAX_DELAY_US);
310 do {
Aaron Durbinc4220022013-07-24 16:02:14 -0500311 u8 value = tpm_read_access(locality);
312 if ((value & mask) == expected)
313 return 0;
Werner Zeh92ab6112021-10-11 15:27:12 +0200314 udelay(1);
315 } while (!stopwatch_expired(&sw));
Aaron Durbinc4220022013-07-24 16:02:14 -0500316 return TPM_TIMEOUT_ERR;
317}
318
319static inline int tis_wait_dropped_access(int locality)
320{
321 return tis_wait_access(locality, TIS_ACCESS_ACTIVE_LOCALITY, 0);
322}
323
324static inline int tis_wait_received_access(int locality)
325{
326 return tis_wait_access(locality, TIS_ACCESS_ACTIVE_LOCALITY,
327 TIS_ACCESS_ACTIVE_LOCALITY);
328}
329
330static inline int tis_has_access(int locality)
331{
332 return !!(tpm_read_access(locality) & TIS_ACCESS_ACTIVE_LOCALITY);
333}
334
335static inline void tis_request_access(int locality)
336{
337 tpm_write_access(TIS_ACCESS_REQUEST_USE, locality);
338}
339
340static inline void tis_drop_access(int locality)
341{
342 tpm_write_access(TIS_ACCESS_ACTIVE_LOCALITY, locality);
343}
344
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700345/*
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700346 * PC Client Specific TPM Interface Specification section 11.2.12:
347 *
348 * Software must be prepared to send two writes of a "1" to command ready
349 * field: the first to indicate successful read of all the data, thus
350 * clearing the data from the ReadFIFO and freeing the TPM's resources,
351 * and the second to indicate to the TPM it is about to send a new command.
352 *
353 * In practice not all TPMs behave the same so it is necessary to be
354 * flexible when trying to set command ready.
355 *
356 * Returns 0 on success if the TPM is ready for transactions.
357 * Returns TPM_TIMEOUT_ERR if the command ready bit does not get set.
358 */
359static int tis_command_ready(u8 locality)
360{
361 u32 status;
362
363 /* 1st 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
366 /* Wait for response */
Aaron Durbinc4220022013-07-24 16:02:14 -0500367 status = tpm_read_status(locality);
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700368
369 /* Check if command ready is set yet */
370 if (status & TIS_STS_COMMAND_READY)
371 return 0;
372
373 /* 2nd attempt to set command ready */
Aaron Durbinc4220022013-07-24 16:02:14 -0500374 tpm_write_status(TIS_STS_COMMAND_READY, locality);
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700375
Aaron Durbinc4220022013-07-24 16:02:14 -0500376 return tis_wait_ready(locality);
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700377}
378
379/*
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700380 * Probe the TPM device and try determining its manufacturer/device name.
381 *
382 * Returns 0 on success (the device is found or was found during an earlier
383 * invocation) or TPM_DRIVER_ERR if the device is not found.
384 */
385static u32 tis_probe(void)
386{
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700387 const char *device_name = "unknown";
388 const char *vendor_name = device_name;
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700389 const struct device_name *dev;
390 u32 didvid;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700391 u16 vid, did;
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700392 int i;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700393
Arthur Heymans0ca944b2019-11-20 19:51:06 +0100394 if (vendor_dev_id)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700395 return 0; /* Already probed. */
396
Aaron Durbinc4220022013-07-24 16:02:14 -0500397 didvid = tpm_read_did_vid(0);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700398 if (!didvid || (didvid == 0xffffffff)) {
Angel Pons08e8cab2020-06-18 15:20:37 +0200399 printf("%s: No TPM device found\n", __func__);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700400 return TPM_DRIVER_ERR;
401 }
402
Arthur Heymans0ca944b2019-11-20 19:51:06 +0100403 vendor_dev_id = didvid;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700404
405 vid = didvid & 0xffff;
406 did = (didvid >> 16) & 0xffff;
407 for (i = 0; i < ARRAY_SIZE(vendor_names); i++) {
408 int j = 0;
409 u16 known_did;
410 if (vid == vendor_names[i].vendor_id) {
411 vendor_name = vendor_names[i].vendor_name;
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700412 } else {
413 continue;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700414 }
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700415 dev = &vendor_names[i].dev_names[j];
416 while ((known_did = dev->dev_id) != 0xffff) {
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700417 if (known_did == did) {
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700418 device_name = dev->dev_name;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700419 break;
420 }
421 j++;
Subrata41b08d92015-05-14 14:38:07 +0530422 dev = &vendor_names[i].dev_names[j];
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700423 }
424 break;
425 }
426 /* this will have to be converted into debug printout */
Duncan Laurie17ba9442015-09-03 16:00:49 -0700427 printk(BIOS_INFO, "Found TPM %s by %s\n", device_name, vendor_name);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700428 return 0;
429}
430
431/*
432 * tis_senddata()
433 *
434 * send the passed in data to the TPM device.
435 *
436 * @data - address of the data to send, byte by byte
437 * @len - length of the data to send
438 *
439 * Returns 0 on success, TPM_DRIVER_ERR on error (in case the device does
440 * not accept the entire command).
441 */
Elyes HAOUASb0b0c8c2018-07-08 12:33:47 +0200442static u32 tis_senddata(const u8 *const data, u32 len)
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700443{
444 u32 offset = 0;
445 u16 burst = 0;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700446 u8 locality = 0;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700447
Aaron Durbinc4220022013-07-24 16:02:14 -0500448 if (tis_wait_ready(locality)) {
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700449 printf("%s:%d - failed to get 'command_ready' status\n",
450 __FILE__, __LINE__);
451 return TPM_DRIVER_ERR;
452 }
Aaron Durbinc4220022013-07-24 16:02:14 -0500453 burst = tpm_read_burst_count(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700454
455 while (1) {
Martin Roth38ddbfb2019-10-23 21:41:00 -0600456 unsigned int count;
Werner Zeh92ab6112021-10-11 15:27:12 +0200457 struct stopwatch sw;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700458
459 /* Wait till the device is ready to accept more data. */
Werner Zeh92ab6112021-10-11 15:27:12 +0200460 stopwatch_init_usecs_expire(&sw, MAX_DELAY_US);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700461 while (!burst) {
Werner Zeh92ab6112021-10-11 15:27:12 +0200462 if (stopwatch_expired(&sw)) {
Werner Zeh66b2f202021-10-12 15:14:22 +0200463 printf("%s:%d failed to feed %u bytes of %u\n",
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700464 __FILE__, __LINE__, len - offset, len);
465 return TPM_DRIVER_ERR;
466 }
467 udelay(1);
Aaron Durbinc4220022013-07-24 16:02:14 -0500468 burst = tpm_read_burst_count(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700469 }
470
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700471 /*
472 * Calculate number of bytes the TPM is ready to accept in one
473 * shot.
474 *
475 * We want to send the last byte outside of the loop (hence
476 * the -1 below) to make sure that the 'expected' status bit
477 * changes to zero exactly after the last byte is fed into the
478 * FIFO.
479 */
Elyes HAOUAS361a9352019-12-18 21:26:33 +0100480 count = MIN(burst, len - offset - 1);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700481 while (count--)
Aaron Durbinc4220022013-07-24 16:02:14 -0500482 tpm_write_data(data[offset++], locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700483
Aaron Durbinc4220022013-07-24 16:02:14 -0500484 if (tis_wait_valid(locality) || !tis_expect_data(locality)) {
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700485 printf("%s:%d TPM command feed overflow\n",
486 __FILE__, __LINE__);
487 return TPM_DRIVER_ERR;
488 }
489
Aaron Durbinc4220022013-07-24 16:02:14 -0500490 burst = tpm_read_burst_count(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700491 if ((offset == (len - 1)) && burst)
492 /*
493 * We need to be able to send the last byte to the
494 * device, so burst size must be nonzero before we
495 * break out.
496 */
497 break;
498 }
499
500 /* Send the last byte. */
Aaron Durbinc4220022013-07-24 16:02:14 -0500501 tpm_write_data(data[offset++], locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700502
503 /*
504 * Verify that TPM does not expect any more data as part of this
505 * command.
506 */
Aaron Durbinc4220022013-07-24 16:02:14 -0500507 if (tis_wait_valid(locality) || tis_expect_data(locality)) {
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700508 printf("%s:%d unexpected TPM status 0x%x\n",
Aaron Durbinc4220022013-07-24 16:02:14 -0500509 __FILE__, __LINE__, tpm_read_status(locality));
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700510 return TPM_DRIVER_ERR;
511 }
512
513 /* OK, sitting pretty, let's start the command execution. */
Aaron Durbinc4220022013-07-24 16:02:14 -0500514 tpm_write_status(TIS_STS_TPM_GO, locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700515
516 return 0;
517}
518
519/*
520 * tis_readresponse()
521 *
522 * read the TPM device response after a command was issued.
523 *
524 * @buffer - address where to read the response, byte by byte.
525 * @len - pointer to the size of buffer
526 *
527 * On success stores the number of received bytes to len and returns 0. On
528 * errors (misformatted TPM data or synchronization problems) returns
529 * TPM_DRIVER_ERR.
530 */
531static u32 tis_readresponse(u8 *buffer, size_t *len)
532{
533 u16 burst_count;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700534 u32 offset = 0;
535 u8 locality = 0;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700536 u32 expected_count = *len;
537 int max_cycles = 0;
538
539 /* Wait for the TPM to process the command */
Aaron Durbinc4220022013-07-24 16:02:14 -0500540 if (tis_wait_valid_data(locality)) {
541 printf("%s:%d failed processing command\n", __FILE__, __LINE__);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700542 return TPM_DRIVER_ERR;
543 }
544
545 do {
Aaron Durbinc4220022013-07-24 16:02:14 -0500546 while ((burst_count = tpm_read_burst_count(locality)) == 0) {
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700547 if (max_cycles++ == MAX_DELAY_US) {
548 printf("%s:%d TPM stuck on read\n",
549 __FILE__, __LINE__);
550 return TPM_DRIVER_ERR;
551 }
552 udelay(1);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700553 }
554
555 max_cycles = 0;
556
557 while (burst_count-- && (offset < expected_count)) {
Aaron Durbinc4220022013-07-24 16:02:14 -0500558 buffer[offset++] = tpm_read_data(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700559 if (offset == 6) {
560 /*
561 * We got the first six bytes of the reply,
562 * let's figure out how many bytes to expect
563 * total - it is stored as a 4 byte number in
564 * network order, starting with offset 2 into
565 * the body of the reply.
566 */
567 u32 real_length;
568 memcpy(&real_length,
569 buffer + 2,
570 sizeof(real_length));
571 expected_count = be32_to_cpu(real_length);
572
573 if ((expected_count < offset) ||
574 (expected_count > *len)) {
Werner Zeh66b2f202021-10-12 15:14:22 +0200575 printf("%s:%d bad response size %u\n",
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700576 __FILE__, __LINE__,
577 expected_count);
578 return TPM_DRIVER_ERR;
579 }
580 }
581 }
582
583 /* Wait for the next portion */
Aaron Durbinc4220022013-07-24 16:02:14 -0500584 if (tis_wait_valid(locality)) {
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700585 printf("%s:%d failed to read response\n",
586 __FILE__, __LINE__);
587 return TPM_DRIVER_ERR;
588 }
589
590 if (offset == expected_count)
591 break; /* We got all we need */
592
Bill XIEa4bf0b72018-03-22 17:07:43 +0800593 /*
594 * Certain TPMs seem to need some delay between tis_wait_valid()
595 * and tis_has_valid_data(), or some race-condition-related
596 * issue will occur.
597 */
Julius Wernercd49cce2019-03-05 16:53:33 -0800598 if (CONFIG(TPM_RDRESP_NEED_DELAY))
Bill XIEa4bf0b72018-03-22 17:07:43 +0800599 udelay(10);
600
Aaron Durbinc4220022013-07-24 16:02:14 -0500601 } while (tis_has_valid_data(locality));
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700602
Aaron Durbinc4220022013-07-24 16:02:14 -0500603 /* * Make sure we indeed read all there was. */
604 if (tis_has_valid_data(locality)) {
Werner Zeh66b2f202021-10-12 15:14:22 +0200605 printf("%s:%d wrong receive status: %x %u bytes left\n",
Aaron Durbinc4220022013-07-24 16:02:14 -0500606 __FILE__, __LINE__, tpm_read_status(locality),
607 tpm_read_burst_count(locality));
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700608 return TPM_DRIVER_ERR;
609 }
610
611 /* Tell the TPM that we are done. */
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700612 if (tis_command_ready(locality) == TPM_TIMEOUT_ERR)
613 return TPM_DRIVER_ERR;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700614
615 *len = offset;
616 return 0;
617}
618
619/*
620 * tis_init()
621 *
622 * Initialize the TPM device. Returns 0 on success or TPM_DRIVER_ERR on
623 * failure (in case device probing did not succeed).
624 */
625int tis_init(void)
626{
627 if (tis_probe())
628 return TPM_DRIVER_ERR;
629 return 0;
630}
631
632/*
633 * tis_open()
634 *
635 * Requests access to locality 0 for the caller. After all commands have been
636 * completed the caller is supposed to call tis_close().
637 *
638 * Returns 0 on success, TPM_DRIVER_ERR on failure.
639 */
640int tis_open(void)
641{
642 u8 locality = 0; /* we use locality zero for everything */
643
644 if (tis_close())
645 return TPM_DRIVER_ERR;
646
647 /* now request access to locality */
Aaron Durbinc4220022013-07-24 16:02:14 -0500648 tis_request_access(locality);
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700649
650 /* did we get a lock? */
Aaron Durbinc4220022013-07-24 16:02:14 -0500651 if (tis_wait_received_access(locality)) {
Werner Zeh66b2f202021-10-12 15:14:22 +0200652 printf("%s:%d - failed to lock locality %u\n",
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700653 __FILE__, __LINE__, locality);
654 return TPM_DRIVER_ERR;
655 }
656
Stefan Reinauerc908fc72012-04-30 16:33:44 -0700657 /* Certain TPMs seem to need some delay here or they hang... */
658 udelay(10);
659
660 if (tis_command_ready(locality) == TPM_TIMEOUT_ERR)
661 return TPM_DRIVER_ERR;
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700662
663 return 0;
664}
665
666/*
667 * tis_close()
668 *
Martin Roth56889792013-07-09 21:39:46 -0600669 * terminate the current session with the TPM by releasing the locked
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700670 * locality. Returns 0 on success of TPM_DRIVER_ERR on failure (in case lock
671 * removal did not succeed).
672 */
673int tis_close(void)
674{
675 u8 locality = 0;
Aaron Durbinc4220022013-07-24 16:02:14 -0500676 if (tis_has_access(locality)) {
677 tis_drop_access(locality);
678 if (tis_wait_dropped_access(locality)) {
Werner Zeh66b2f202021-10-12 15:14:22 +0200679 printf("%s:%d - failed to release locality %u\n",
Stefan Reinauer3008bbad2011-10-11 14:46:25 -0700680 __FILE__, __LINE__, locality);
681 return TPM_DRIVER_ERR;
682 }
683 }
684 return 0;
685}
686
687/*
688 * tis_sendrecv()
689 *
690 * Send the requested data to the TPM and then try to get its response
691 *
692 * @sendbuf - buffer of the data to send
693 * @send_size size of the data to send
694 * @recvbuf - memory to save the response to
695 * @recv_len - pointer to the size of the response buffer
696 *
697 * Returns 0 on success (and places the number of response bytes at recv_len)
698 * or TPM_DRIVER_ERR on failure.
699 */
700int tis_sendrecv(const uint8_t *sendbuf, size_t send_size,
701 uint8_t *recvbuf, size_t *recv_len)
702{
703 if (tis_senddata(sendbuf, send_size)) {
704 printf("%s:%d failed sending data to TPM\n",
705 __FILE__, __LINE__);
706 return TPM_DRIVER_ERR;
707 }
708
709 return tis_readresponse(recvbuf, recv_len);
710}
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700711
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700712/*
713 * tis_setup_interrupt()
714 *
715 * Set up the interrupt vector and polarity for locality 0 and
716 * disable all interrupts so they are unused in firmware but can
717 * be enabled by the OS.
718 *
719 * The values used here must match what is passed in the TPM ACPI
720 * device if ACPI is used on the platform.
721 *
722 * @vector - TPM interrupt vector
723 * @polarity - TPM interrupt polarity
724 *
725 * Returns 0 on success, TPM_DRIVER_ERR on failure.
726 */
727static int tis_setup_interrupt(int vector, int polarity)
728{
729 u8 locality = 0;
730 int has_access = tis_has_access(locality);
731
732 /* Open connection and request access if not already granted */
733 if (!has_access && tis_open() < 0)
734 return TPM_DRIVER_ERR;
735
736 /* Set TPM interrupt vector */
737 tpm_write_int_vector(vector, locality);
738
Elyes HAOUAS18958382018-08-07 12:23:16 +0200739 /* Set TPM interrupt polarity and disable interrupts */
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700740 tpm_write_int_polarity(polarity, locality);
741
742 /* Close connection if it was opened */
743 if (!has_access && tis_close() < 0)
744 return TPM_DRIVER_ERR;
745
746 return 0;
747}
748
749static void lpc_tpm_read_resources(struct device *dev)
750{
751 /* Static 5K memory region specified in Kconfig */
Kyösti Mälkki27d62992022-05-24 20:25:58 +0300752 mmio_resource_kb(dev, 0, CONFIG_TPM_TIS_BASE_ADDRESS >> 10, 0x5000 >> 10);
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700753}
754
755static void lpc_tpm_set_resources(struct device *dev)
756{
757 tpm_config_t *config = (tpm_config_t *)dev->chip_info;
Kyösti Mälkkibaa16e92019-11-05 18:38:00 +0200758 DEVTREE_CONST struct resource *res;
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700759
760 for (res = dev->resource_list; res; res = res->next) {
761 if (!(res->flags & IORESOURCE_ASSIGNED))
762 continue;
763
764 if (res->flags & IORESOURCE_IRQ) {
765 /* Set interrupt vector */
766 tis_setup_interrupt((int)res->base,
767 config->irq_polarity);
768 } else {
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700769 continue;
770 }
771
Kyösti Mälkkibaa16e92019-11-05 18:38:00 +0200772#if !DEVTREE_EARLY
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700773 res->flags |= IORESOURCE_STORED;
774 report_resource_stored(dev, res, " <tpm>");
Kyösti Mälkkibaa16e92019-11-05 18:38:00 +0200775#endif
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700776 }
777}
778
Julius Wernercd49cce2019-03-05 16:53:33 -0800779#if CONFIG(HAVE_ACPI_TABLES)
Furquan Shaikh7536a392020-04-24 21:59:21 -0700780static void lpc_tpm_fill_ssdt(const struct device *dev)
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530781{
Michał Kopeća691cbd2022-02-22 12:30:22 +0100782 /* Windows 11 requires the following path for TPM to be detected */
783 const char *path = "\\_SB_.PCI0";
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530784
785 /* Device */
786 acpigen_write_scope(path);
787 acpigen_write_device(acpi_device_name(dev));
788
Michał Żygowski7b288012020-03-20 15:41:44 +0100789 if (CONFIG(TPM2)) {
790 acpigen_write_name_string("_HID", "MSFT0101");
791 acpigen_write_name_string("_CID", "MSFT0101");
792 } else {
793 acpigen_write_name("_HID");
794 acpigen_emit_eisaid("PNP0C31");
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530795
Michał Żygowski7b288012020-03-20 15:41:44 +0100796 acpigen_write_name("_CID");
797 acpigen_emit_eisaid("PNP0C31");
798 }
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530799
Patrick Rudolphc83bab62019-12-13 12:16:06 +0100800 acpi_device_write_uid(dev);
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530801
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530802 u32 did_vid = tpm_read_did_vid(0);
803 if (did_vid > 0 && did_vid < 0xffffffff)
804 acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON);
805 else
806 acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_OFF);
807
Kevin Cody-Littlec97b5af2018-05-09 14:14:59 -0400808 u16 port = dev->path.pnp.port;
809
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530810 /* Resources */
811 acpigen_write_name("_CRS");
812 acpigen_write_resourcetemplate_header();
813 acpigen_write_mem32fixed(1, CONFIG_TPM_TIS_BASE_ADDRESS, 0x5000);
Frans Hendriks6cc937e2018-10-29 14:30:58 +0100814 if (port)
815 acpigen_write_io16(port, port, 1, 2, 1);
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530816
817 if (CONFIG_TPM_PIRQ) {
818 /*
819 * PIRQ: Update interrupt vector with configured PIRQ
820 * Active-Low Level-Triggered Shared
821 */
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800822 struct acpi_irq tpm_irq_a = ACPI_IRQ_LEVEL_LOW(CONFIG_TPM_PIRQ);
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530823 acpi_device_write_interrupt(&tpm_irq_a);
824 } else if (tpm_read_int_vector(0) > 0) {
825 u8 int_vec = tpm_read_int_vector(0);
826 u8 int_pol = tpm_read_int_polarity(0);
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800827 struct acpi_irq tpm_irq = ACPI_IRQ_LEVEL_LOW(int_vec);
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530828
829 if (int_pol & 1)
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800830 tpm_irq.polarity = ACPI_IRQ_ACTIVE_LOW;
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530831 else
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800832 tpm_irq.polarity = ACPI_IRQ_ACTIVE_HIGH;
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530833
834 if (int_pol & 2)
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800835 tpm_irq.mode = ACPI_IRQ_EDGE_TRIGGERED;
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530836 else
Furquan Shaikh5b9b5932017-02-21 13:16:30 -0800837 tpm_irq.mode = ACPI_IRQ_LEVEL_TRIGGERED;
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530838
839 acpi_device_write_interrupt(&tpm_irq);
840 }
841
Patrick Rudolphd8d8be12020-09-21 09:48:53 +0200842
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530843 acpigen_write_resourcetemplate_footer();
844
Patrick Rudolphd8d8be12020-09-21 09:48:53 +0200845 if (!CONFIG(CHROMEOS))
846 tpm_ppi_acpi_fill_ssdt(dev);
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530847
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530848 acpigen_pop_len(); /* Device */
849 acpigen_pop_len(); /* Scope */
850
Kyösti Mälkkibaa16e92019-11-05 18:38:00 +0200851#if !DEVTREE_EARLY
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530852 printk(BIOS_INFO, "%s.%s: %s %s\n", path, acpi_device_name(dev),
853 dev->chip_ops->name, dev_path(dev));
Kyösti Mälkkibaa16e92019-11-05 18:38:00 +0200854#endif
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530855}
856
Aaron Durbinaa090cb2017-09-13 16:01:52 -0600857static const char *lpc_tpm_acpi_name(const struct device *dev)
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530858{
859 return "TPM";
860}
861#endif
862
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700863static struct device_operations lpc_tpm_ops = {
Elyes HAOUAS2aa3b162018-11-27 17:02:10 +0100864 .read_resources = lpc_tpm_read_resources,
865 .set_resources = lpc_tpm_set_resources,
Julius Wernercd49cce2019-03-05 16:53:33 -0800866#if CONFIG(HAVE_ACPI_TABLES)
Nico Huber68680dd2020-03-31 17:34:52 +0200867 .acpi_name = lpc_tpm_acpi_name,
868 .acpi_fill_ssdt = lpc_tpm_fill_ssdt,
Naresh G Solanki80ff0382016-11-15 11:01:33 +0530869#endif
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700870};
871
872static struct pnp_info pnp_dev_info[] = {
873 { .flags = PNP_IRQ0 }
874};
875
876static void enable_dev(struct device *dev)
877{
Kyösti Mälkkid2b2a182021-04-29 15:33:07 +0300878 if (CONFIG(TPM))
Kyösti Mälkki9cc64932020-05-29 19:42:07 +0300879 pnp_enable_devices(dev, &lpc_tpm_ops,
880 ARRAY_SIZE(pnp_dev_info), pnp_dev_info);
Duncan Lauriedd281ed2014-10-30 15:20:19 -0700881}
882
883struct chip_operations drivers_pc80_tpm_ops = {
884 CHIP_NAME("LPC TPM")
885 .enable_dev = enable_dev
886};